ddl-auto : JPA는 테이블을 자동으로 생성하는 기능을 제공하는데 none 를 사용하면 해당 기능을 끈다. create 를 사용하면 엔티티 정보를 바탕으로 테이블도 직접 생성해준다. 해보자.
3. JPA 엔티티 매핑
/com.example.demo/domain/Member
package com.example.demo.domain;
import javax.persistence.*;
@Entity
public class Member {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
4. JpaMemberRepository 생성
/repository/JpaMemberRepository
package com.example.demo.repository;
import com.example.demo.domain.Member;
import javax.persistence.EntityManager;
import java.util.List;
import java.util.Optional;
public class JpaMemberRepository implements MemberRepository {
private final EntityManager em;
public JpaMemberRepository(EntityManager em) {
this.em = em;
}
@Override
public Member save(Member member) {
em.persist(member);
return null;
}
@Override
public Optional<Member> findById(Long id) {
Member member = em.find(Member.class, id);
return Optional.ofNullable(member);
}
@Override
public Optional<Member> findByName(String name) {
List<Member> result = em.createQuery("select m from Member m where m.name = :name", Member.class)
.setParameter("name", name)
.getResultList();
return result.stream().findAny();
}
@Override
public List<Member> findAll() {
return em.createQuery("select m from Member m", Member.class)
.getResultList();
}
}
5. 서비스 계층에 트랜잭션 추가
import org.springframework.transaction.annotation.Transactional;
@Transactional
public class MemberService {
스프링은 해당 클래스의 메서드를 실행할 때 트랜잭션을 시작하고, 메서드가 정상 종료되면 트랜잭션을 커밋한다. 만약 런타임 예외가 발생하면 롤백한다.
여기서 @ResponseBody 는 API 응답으로 Body 안에 원하는 데이터를 넣어주겠다는 뜻이다.
브라우저에서 접속해보면
return 값이 정상적으로 반환되는 것을 확인할 수 있다.
2. JSON return Controller
json 형식으로 반환할 땐 아래와 같이 할 수 있다.
@GetMapping("teepo-api")
@ResponseBody
public Teepo teepoApi(@RequestParam("name") String name) {
Teepo teepo = new Teepo();
teepo.setName(name);
return teepo;
}
static class Teepo {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
서버를 키고 확인해보면,
이와같이 json 형식으로 데이터를 받은 것을 확인할 수 있다.
3. Member 객체 생성
domain 폴더를 만들고 Member 에 대한 객체를 만들어보자.
com.example.demo/domain/Member
package com.example.demo.domain;
public class Member {
private Long id;
private String name;
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
package com.example.demo.repository;
import com.example.demo.domain.Member;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;
import java.util.List;
import static org.assertj.core.api.Assertions.*;
class MemoryMemberRepositoryTest {
MemoryMemberRepository repository = new MemoryMemberRepository();
@AfterEach
public void afterEach() {
repository.clearStore();
}
@Test
public void save() {
//given
Member member = new Member();
member.setName("spring");
//when
repository.save(member);
//then
Member result = repository.findById(member.getId()).get();
assertThat(result).isEqualTo(member);
}
@Test
public void findByName() {
//given
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring2");
repository.save(member2);
//when
Member result = repository.findByName("spring1").get();
//then
assertThat(result).isEqualTo(member1);
}
@Test
public void findAll() {
//given
Member member1 = new Member();
member1.setName("spring1");
repository.save(member1);
Member member2 = new Member();
member2.setName("spring2");
repository.save(member2);
//when
List<Member> result = repository.findAll();
//then
assertThat(result.size()).isEqualTo(2);
}
}
// dotenv
import dotenv from 'dotenv'
dotenv.config();
// modules
import { By, until, WebDriver } from 'selenium-webdriver';
// logger
import { logger } from '../../config/winston';
// node-json-db
import { db } from '../../config/nodejsondb';
import {
getDriverHandler,
alertCloseAccept,
alertCloseDismiss,
promptCloseHandler,
addCookie,
getOneCookie,
getAllCookie,
deleteOneCookie,
deleteAllCookie,
fileRegister,
findElementById,
findElementByName,
findElementByXpath,
findElementByCss,
JqChangeValueByID,
JqRemoveAttribute,
naviGet,
naviBack,
naviForward,
naviRefresh,
popupClose
} from '../../modules';
describe('GET /one', () => {
// 웹드라이버 설정
let driver : WebDriver;
beforeAll(async () => {
driver = await getDriverHandler();
})
afterAll(async () => {
await driver.quit()
})
test('url 잘 뜨는지 확인', async () => {
try {
// 브라우저에 접속
await driver.get('https://typo.tistory.com/');
// 현재 주소 가져오기
const text = await driver.getCurrentUrl();
// test
expect(text).toEqual('https://typo.tistory.com/')
}
catch(Err) {
console.log(Err)
logger.debug(Err)
throw Error;
}
})
})
/tests/middlewares/http.test.ts
import express, { Request, Response, NextFunction } from 'express';
import {
loggerInfo,
loggerError,
loggerHttp,
loggerDebug,
} from '../../config/winston'
import { httpLoggingMiddleware } from '../../middlewares';
describe('MiddleWare http', () => {
let mockRequest : Partial<Request>;
let mockResponse : Partial<Response>;
let nextFunction: NextFunction = jest.fn();
beforeEach(()=> {
mockRequest = {};
mockResponse = {
json: jest.fn()
};
});
test('http 로깅 미들웨어 테스트', async () => {
try {
await httpLoggingMiddleware(mockRequest as Request, mockResponse as Response, nextFunction);
// nextFunction 까지 잘되는지 확인
expect(nextFunction).toBeCalledTimes(1);
}
catch (Err) {
loggerDebug.info(JSON.stringify(Err))
}
})
})
reqest, response, next 를 mock으로 만들어 테스트해준다.
/tests/middlewares/timeInterval.ts
import express, { Request, Response, NextFunction } from 'express';
import {
loggerInfo,
loggerError,
loggerHttp,
loggerDebug,
} from '../../config/winston'
import { timeIntervalMiddleware } from '../../middlewares';
describe('MiddleWare timeInterval', () => {
let mockRequest : Partial<Request>;
let mockResponse : Partial<Response>;
let nextFunction: NextFunction = jest.fn();
beforeEach(()=> {
mockRequest = { originalUrl : '/one' };
mockResponse = {
json: jest.fn()
};
});
test('timeInterval 로깅 미들웨어 테스트', async () => {
try {
await timeIntervalMiddleware(mockRequest as Request, mockResponse as Response, nextFunction);
// nextFunction 까지 잘되는지 확인
expect(nextFunction).toBeCalledTimes(1);
}
catch (Err) {
loggerDebug.info(JSON.stringify(Err))
}
})
})
/tests/modules/db/expireHandler.ts
import express, { Request, Response, NextFunction } from 'express';
// node-json-db
import { db } from '../../../config/nodejsondb';
// logger
import { loggerDebug } from '../../../config/winston';
import { expireHandler } from '../../../modules';
describe('Module expireHandler', () => {
let dbPath : string;
beforeEach(()=> {
dbPath = '/one'
});
test('30분 지난 데이터 삭제 및 확인', async () => {
try {
let nowTime = new Date(); // 현재 시간
// 30분 지난 데이터 삭제
await expireHandler(dbPath);
// 데이터 베이스 안 데이터들
const datalist = await db.getData(dbPath);
let index = 0; // 기준점이 되는 index
// 데이터 매핑 및 시간 차이 계산해서 30분 지난 데이터는 삭제
await datalist.map(async (item : any, index2: number) => {
let beforeTime = new Date(item.date); // 데이터들 저장된 시간
let diff = (nowTime.getTime() - beforeTime.getTime()) / (1000*60); // 데이터들 시간 차이
// 저장된 지 30분이 지나면 index 체크
if(diff > 30) {
index = index2;
}
})
// ----- 30분 지난 데이터 없는지 확인 -----
expect(index).toEqual(0);
}
catch(Err) {
console.log("Err : ",Err)
loggerDebug.info(JSON.stringify(Err))
}
})
})
/tests/modules/selenium/alertHandler.test.ts
// dotenv
import dotenv from 'dotenv'
dotenv.config();
// modules
import { By, until, WebDriver } from 'selenium-webdriver';
// logger
import {
loggerDebug,
} from '../../../config/winston';
import {
getDriverHandler,
findElementById,
} from '../../../modules';
// window type 선언
declare const window: typeof globalThis;
describe('Module alertHandler', () => {
// 웹드라이버 설정
let driver : WebDriver;
beforeEach(async () => {
driver = await getDriverHandler();
await driver.get('https://testpages.herokuapp.com/styled/alerts/alert-test.html')
})
afterEach(async () => {
await driver.quit()
})
test('alert 테스트 ( alert 텍스트가 잘 나오는지 )', async () => {
try {
await (await findElementById(driver,'alertexamples')).click();
// alert 창 뜰 때까지 기다림
await driver.wait(until.alertIsPresent());
// alert 로 드라이버 이동
let alert = await driver.switchTo().alert();
// alertText
let alertText = await alert.getText();
// 확인버튼 클릭
await alert.accept();
// 다시 원래 컨텐츠로 드라이버 이동
await driver.switchTo().defaultContent();
expect(alertText).toEqual('I am an alert box!');
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
})
test('prompt 테스트', async () => {
try {
await (await findElementById(driver,'promptexample')).click();
// alert 창 뜰 때까지 기다림
await driver.wait(until.alertIsPresent());
// alert 로 드라이버 이동
let alert = await driver.switchTo().alert();
// alert text 사용하는 곳
let alertText = await alert.getText();
// 확인버튼 클릭
await alert.accept();
// 다시 원래 컨텐츠로 드라이버 이동
await driver.switchTo().defaultContent();
expect(alertText).toEqual('I prompt you');
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
})
})
/tests/modules/selenium/cookieHandler.test.ts
// dotenv
import dotenv from 'dotenv'
dotenv.config();
// modules
import { By, until, WebDriver } from 'selenium-webdriver';
// logger
import {
loggerDebug,
} from '../../../config/winston';
import {
getDriverHandler,
addCookie,
getOneCookie,
getAllCookie,
deleteOneCookie,
findElementById,
findElementByName,
} from '../../../modules';
describe('Module cookieHandler', () => {
// 웹드라이버 설정
let driver : WebDriver;
beforeEach(async () => {
driver = await getDriverHandler();
await driver.get('https://testpages.herokuapp.com/styled/cookies/adminlogin.html');
// 로그인 및 쿠키생성
await (await findElementByName(driver,'username')).sendKeys('Admin');
await (await findElementByName(driver,'password')).sendKeys('AdminPass');
await (await findElementById(driver,'login')).click();
},15000)
afterEach(async () => {
await driver.quit()
})
test('쿠키 추가하기', async () => {
try {
// 쿠키추가
await addCookie(driver,{name : "name", value : "teepo"});
// 쿠키 가져오기
const cookies = (await driver.manage().getCookie('name')).value;
// 쿠키 확인
expect(cookies).toEqual('teepo');
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
})
test('쿠키 하나 가져오기', async () => {
try {
// 쿠키 가져오기
const result = await getOneCookie(driver,'loggedin');
// 쿠키 확인
expect(result.value).toEqual('Admin');
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
})
test('쿠키 전부 가져오기', async () => {
try {
// 쿠키 가져오기
const result = await getAllCookie(driver);
// 쿠키 확인
expect(result[0].value).toEqual('Admin');
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
})
test('쿠키 하나 지우기', async () => {
try {
// 쿠키 삭제하기
await deleteOneCookie(driver,'loggedin');
// 쿠키 확인
const result = await getAllCookie(driver);
// 쿠키가 없어졌는지 확인
expect(result.length).toEqual(0);
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
})
test('쿠키 전부 지우기', async () => {
try {
// 쿠키 삭제하기
await deleteOneCookie(driver,'loggedin');
// 쿠키 확인
const result = await getAllCookie(driver);
// 쿠키가 없어졌는지 확인
expect(result.length).toEqual(0);
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
})
})
// dotenv
import dotenv from 'dotenv'
dotenv.config();
// modules
import { By, until, WebDriver } from 'selenium-webdriver';
// logger
import {
loggerDebug,
} from '../../../config/winston';
import {
getDriverHandler,
findElementById,
findElementsById,
findElementByName,
findElementByXpath,
findElementByClass,
findElementsByName,
findElementsByXpath,
findElementsByClass
} from '../../../modules';
describe('Module findElementHandler', () => {
// 웹드라이버 설정
let driver : WebDriver;
beforeAll(async () => {
driver = await getDriverHandler();
driver.get('https://testpages.herokuapp.com/styled/find-by-playground-test.html')
})
afterAll(async () => {
await driver.quit()
})
test('findElementById', async () => {
try {
let text = await (await findElementById(driver,'p1')).getText();
expect(text).toEqual('This is a paragraph text')
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
});
test('findElementByName', async () => {
try {
let text = await (await findElementByName(driver,'pName1')).getText();
expect(text).toEqual('This is a paragraph text')
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
});
test('findElementByXpath', async () => {
try {
let text = await (await findElementByXpath(driver,'//*[@id="p1"]')).getText();
expect(text).toEqual('This is a paragraph text')
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
});
test('findElementByClass', async () => {
try {
let text = await (await findElementByClass(driver,'explanation')).getText();
expect(text).toEqual('This is a set of nested elements. There are various ways to locate each of the elements. Challenge yourself to find as many ways of locating elements as possible.')
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
});
})
describe('Module findElementsHandler', () => {
// 웹드라이버 설정
let driver : WebDriver;
beforeAll(async () => {
driver = await getDriverHandler();
await driver.get('https://testpages.herokuapp.com/styled/find-by-playground-test.html')
})
afterAll(async () => {
await driver.close()
})
test('findElementsById', async () => {
try {
let text = await (await findElementsById(driver,'p1'))[0].getText();
expect(text).toEqual('This is a paragraph text')
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
});
test('findElementsByName', async () => {
try {
let text = await (await findElementsByName(driver,'pName1'))[0].getText();
expect(text).toEqual('This is a paragraph text')
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
});
test('findElementsByXpath', async () => {
try {
let text = await (await findElementsByXpath(driver,'//*[@id="p1"]'))[0].getText();
expect(text).toEqual('This is a paragraph text')
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
});
test('findElementsByClass', async () => {
try {
let text = await (await findElementsByClass(driver,'explanation'))[0].getText();
expect(text).toEqual('This is a set of nested elements. There are various ways to locate each of the elements. Challenge yourself to find as many ways of locating elements as possible.');
}
catch(Err) {
loggerDebug.info(JSON.stringify(Err))
}
});
})
import webdriver, { WebDriver } from 'selenium-webdriver';
import chrome from 'selenium-webdriver/chrome';
import firefox from 'selenium-webdriver/firefox'
// dotenv
import dotenv from 'dotenv'
dotenv.config();
// new driver 반환 함수
export const getDriverHandler = async () : Promise<WebDriver> => {
const driver = await new webdriver.Builder()
.forBrowser('chrome')
.setChromeOptions(new chrome.Options().windowSize({width: 1920, height: 1080}))
.setChromeService(new chrome.ServiceBuilder(process.env.CHROMEDRIVER_PATH))
.build();
return driver;
}
/modules/index.ts
export * from "./db/expireHandler"
export * from "./selenium/driverHandler"
2. 라우터에서 모듈을 가져와 쓴다.
...
// modules
import {
getDriverHandler,
} from '../modules';
...
// 웹드라이버 설정
let driver = await getDriverHandler();
이런식으로 여러개의 모듈을 만들 수 있다.
/modules/selenium/alertHandler.ts
import { until, WebDriver } from "selenium-webdriver";
// alert 확인 누르기
export const alertCloseAccept = async (driver : WebDriver ) => {
// alert 창 뜰 때까지 기다림
await driver.wait(until.alertIsPresent());
// alert 로 드라이버 이동
let alert = await driver.switchTo().alert();
// alert text 사용하는 곳
let alertText = await alert.getText();
// 확인버튼 클릭
await alert.accept();
// 다시 원래 컨텐츠로 드라이버 이동
await driver.switchTo().defaultContent();
}
// alert 닫기 누르기
export const alertCloseDismiss = async (driver : WebDriver ) => {
// alert 창 뜰 때까지 기다림
await driver.wait(until.alertIsPresent());
// alert 로 드라이버 이동
let alert = await driver.switchTo().alert();
// alert text 사용하는 곳
let alertText = await alert.getText();
// 확인버튼 클릭
await alert.dismiss();
// 다시 원래 컨텐츠로 드라이버 이동
await driver.switchTo().defaultContent();
}
// prompt 핸들러
export const promptCloseHandler = async (driver : WebDriver, text : string) => {
// alert 창 뜰 때까지 기다림
await driver.wait(until.alertIsPresent());
// alert 로 드라이버 이동
let alert = await driver.switchTo().alert();
// prompt text 전송
await alert.sendKeys(text)
// 확인버튼 클릭
await alert.accept();
// 다시 원래 컨텐츠로 드라이버 이동
await driver.switchTo().defaultContent();
}
export * from "./db/expireHandler"
export * from "./selenium/alertHandler"
export * from "./selenium/cookieHandler"
export * from "./selenium/driverHandler"
export * from "./selenium/fileHandler"
export * from "./selenium/jqueryHandler"
export * from "./selenium/navigationHandler"
export * from "./selenium/popupHandler"
export * from "./selenium/findHandler"
export * from "./selenium/sleepHandler"
$ npm i node-json-db shortid @types/shortid --save
2. config 폴더 아래 db 설정을 해준다.
/config/nodejsondb.ts
import { JsonDB } from 'node-json-db';
import { Config } from 'node-json-db/dist/lib/JsonDBConfig'
const db = new JsonDB(new Config("myDataBase", true, false, '/'));
db.push('/one/lastdate', "");
db.push('/one/data',[]);
export {db}
3. 라우터에 결과값을 저장하는 메소드를 사용해준다.
추가로 검색한 데이터는 로그로 남겨둔다.
/routers/one.ts
import express, { Request, Response, NextFunction } from 'express';
var router = express.Router();
// middlewares
import { httpLoggingMiddleware } from '../middlewares';
// logger
import {
loggerHttp,
loggerDebug,
loggerError,
loggerInfo
} from '../config/winston';
// selenium
import webdriver from 'selenium-webdriver';
import chrome from 'selenium-webdriver/chrome';
// node-json-db
import { db } from '../config/nodejsondb';
router.get('/',httpLoggingMiddleware, async function (req: Request, res: Response, next: NextFunction) {
// 웹드라이버 설정
let driver = await new webdriver.Builder()
.forBrowser('chrome')
.setChromeOptions(new chrome.Options().windowSize({width: 1920, height: 1080}))
.setChromeService(new chrome.ServiceBuilder(process.env.CHROMEDRIVER_PATH))
.build()
try {
// 브라우저에 접속
await driver.get('https://typo.tistory.com/')
// 현재 주소 가져오기
const text = await driver.getCurrentUrl();
// 저장 데이터
const savedata = {
id : shortid.generate(),
date : new Date,
result : text
}
// 데이터베이스에 저장
db.push('/one[]', savedata);
// log 저장
loggerInfo.info(JSON.stringify(savedata))
res.send({success: true, result : text});
}
catch(Err) {
loggerError.info(JSON.stringify(Err))
console.log(Err)
driver.close();
res.send({success: false, result : Err})
}
});
export default router;
여기서 중요한 점은 db에서 접근하는 경로 "/one" 와 라우터의 경로인 "/one"( '/'제외 )의 문자열를 일치하게 만들었다는 점이다.
이 방법으로 우린 미들웨어에서 라우터의 경로에 맞게 db에 손쉽게 접근할 수 있다. ( request.originalUrl 사용 )
아직은 이해가 안 될 수 있지만 차근차근 알게 될 것이다.
보통 크롤링 api 서버를 열어두고 제한을 안두면 무분별하게 라우터에 접속해서 userAgent 또는 접속 IP가 막힐 수도 있다.
때문에 userAgent를 바꿔주거나 IP를 우회하는 방법도 있지만 살짝 불법 느낌이 있어서 여기서는 전에 접속했던 시간을 비교해서 5분 간격으로 크롤링이 가능하게끔 만들어보겠다.
방법은 미들웨어에서 url path와 db path를 비교해서 전에 크롤링했던 데이터의 시간을 보고, 5분이 넘었는지 안넘었는지 확인 후
만약 5분이 안지난 상태면 res.send 메소드를 사용해 실패 메세지를 전송하도록 하겠다.
4. timeInterval 미들웨어를 만들어준다.
/middlewares/timeInterval.ts
import express, { Request, Response, NextFunction } from 'express';
const {
loggerInfo,
loggerError,
loggerHttp,
loggerDebug,
} = require('../config/winston');
// node-json-db
import { db } from '../config/nodejsondb';
// module
import { expireHandler } from '../modules';
export const timeIntervalMiddleware = async (req: Request, res: Response, next: NextFunction) => {
try {
// url
let url = req.originalUrl;
// 30분이 지난 데이터는 지워줌
expireHandler(url);
// 전체 데이터베이스 조회
const data = await db.getData('/');
// 처음 크롤링 할 때는 key값 ( 데이터베이스 주소 )이 없는 상태이므로 패스
// 라우터 경로에서 '/'를 뺀 값이랑 데이터베이스의 키 값이랑 같다.
if(!Object.keys(data).includes(url.split('/')[1]) || !(await db.getData(url + "[-1]"))) {
next();
}
// 데이터가 존재할 경우 시간을 현 시간과 비교함
else {
expireHandler(url);
// 현재 시간
let nowTime = new Date()
let beforeTime = new Date(await (await db.getData(url + "[-1]")).date); // 제일 최신 데이터의 조회 시간
let diff = (nowTime.getTime() - beforeTime.getTime()) / (1000*60); // 분 으로 계산한다.
// 전 시간과 비교하여 차이가 5분 미만일 때
if (diff < 5) {
res.send({success: false, result: "5분 미만입니다."})
}
else {
next();
}
}
// next();
}
catch (Err) {
console.log(Err)
loggerError.info(Err)
res.send({ success: false, result: "timeInterverError" });
}
}
/middlewares/index.ts
export * from "./winston"
export * from "./timeInterval"
node-json-db 에는 expire을 설정해주는 메소드가 없는거 같다. 때문에 일정 시간이 지난 데이터를 지워주는 모듈을 만들어봤다.
모듈화에 대해선 다음 포스트에서 제대로 다루겠다.
/modules/db/expireHandler.ts
// node-json-db
import { db } from '../../config/nodejsondb';
// logger
import { loggerError } from '../../config/winston';
export const expireHandler = async (dbPath: string) => {
try {
let nowTime = new Date(); // 현재 시간
// 데이터 베이스 안 데이터들
const datalist = await db.getData(dbPath);
let index = 0; // 기준점이 되는 index
// 데이터 매핑 및 시간 차이 계산해서 30분 지난 데이터는 삭제
await datalist.map(async (item : any, index2: number) => {
let beforeTime = new Date(item.date); // 데이터들 저장된 시간
let diff = (nowTime.getTime() - beforeTime.getTime()) / (1000*60); // 데이터들 시간 차이
// 저장된 지 30분이 지나면 index 체크
if(diff > 30) {
index = index + 1;
}
})
// 설정한 시간을 기준점으로 잡고 그 이전에 만든 데이터를 다 삭제한다. (앞에서부터 shift)
for (let i = 0; i < index; i ++) {
await db.delete(dbPath + '/data[0]');
}
}
catch(Err) {
loggerError.info(JSON.stringify(Err))
}
}
from rest_framework import serializers
from .models import ChwideukModel
class ChwideukSerializer(serializers.ModelSerializer):
class Meta:
model = ChwideukModel
fields = ['title']
8. views.py 파일을 수정해준다.
from rest_framework.views import APIView
from rest_framework.response import Response
class ChwideukRouter(APIView):
def get(self, request):
return Response({'success': True})
def post(self, request):
return Response({'success': False})
9. apiServer 폴더의 urls.py에 방금 만든 라우터를 추가해준다.
from django.contrib import admin
from django.urls import include, path
from chwideukapp.views import ChwideukRouter
urlpatterns = [
path('admin/', admin.site.urls),
path('api/chwideuk/', ChwideukRouter.as_view())
]