컨트롤러는 다음과 같이 밸리데이션 모듈을 이용하여 어떤 파라미터에 대해서 체크를 할 것인지를 정한 후, 마지막으로 async 블록으로 함수를 선언하여 해당하는 요청에 대한 로직 및 예외처리를 작성합니다.
export const controllerFunction1 = [
validate.json,
validateHandler,
async (req: Request, res: Response) => { ... }
]
이어서 라우터에 매핑하기 위해서는 아래와 같이 작성할 수 있습니다.
import express from "express";
import {
controllerFunction1
} from "./user.controller";
const sampleRouter = express.Router();
sampleRouter
.route("/controllerFunction1")
.post(controllerFunction1);
export default sampleRouter;
이와같은 방법으로 인증에 관한 컨트롤러를 작성해 보겠습니다.
컨트롤러를 작성하기 이전에 성공과 실패의 경우에 대해서 다음과 같이 정의할 필요가 있습니다. 다음과 같이 우선 정리를 해 두면 테스트코드를 작성하기에 훨씬 수월하기 때문입니다.
성공 시
201 - 계정생성 완료
실패 시
400 - 이미 로그인된 상태
409 - 이미 존재하는 이메일 입력
500 - 입력된 데이터가 데이터베이스에 저장되지 않은 경우
500 - 서버 에러 (여러가지 요인이 존재함)
자! 그러면 위에 정의된 코드를 기준으로 테스트코드를 작성해 보겠습니다. 원래는 테스트코드를 작성하기 위해서는 훨씬 효율적인 방법이 있다고 생각하지만, 저는 아직 실무경험이 부족하기 때문에 투박하게 작성해보겠습니다.
저는 총 4개의 단계로 나누어서 작성을 합니다.
- Data 영역에서는 데이터베이스에 등록하거나 수정하거나 할 객체를 미리 선언해 둡니다.
- Request 영역에서는 애플리케이션에 해당하는 요청을 보내고 응답 객체를 리턴받습니다.
- Result 영역에서는 테스트 관련 함수를 통해 응답받은 객체에에 대해서 예상된 결과가 나왔는지를 검증합니다.
- Reset 영역에서는 테스트에서 사용된 데이터를 지워줍니다.
describe("POST - /signup", function () {
describe("성공 시", function(){
it("201 - 계정 생성 완료", async function () {
// Data
const data = { email: 'signup@rollback.com', password: 'Test1234!' };
// Request
const response = await server.post("/oauth2/signup").withCredentials().send(data);
// Result
const user = await User.findOne({ email: data.email }).then(e => e);
expect(user).not.null;
expect(response.status).to.equal(201);
// Reset
await User.deleteOne({ email: data.email });
});
})
describe("실패 시", function(){
it("400 - 이미 로그인된 상태", async function(){
// Data
const data = { email: 'signup1@rollback.com', password: 'Test1234!' };
const newData = { email: 'signup2@rollback.com', password: 'Test1234!' };
// Request
await server.post("/oauth2/signup").withCredentials().send(data);
await server.post("/oauth2/signin").withCredentials().send(data);
const response = await server.post("/oauth2/signup").withCredentials().send(newData).then(e=>e);
// Result
expect(response.status).to.equal(400);
// Reset
await server.get("/oauth2/signout").withCredentials();
await User.deleteMany({ email: { $in: [ data.email, newData.email ] } });
})
it("409 - 이미 존재하는 이메일", async function () {
// Data
const data = { email: 'signup1@rollback.com', password: 'Test1234!' };
// Request
await server.post("/oauth2/signup").withCredentials().send(data);
const response = await server.post("/oauth2/signup").withCredentials().send(data);
// Result
expect(response.status).to.equal(409);
// Reset
await User.deleteOne({ email: data.email });
});
it("400 - 이메일이 형식에 맞지 않음", async function () {
// Data
const data = { email: 'signup1rollback.com', password: 'Test1234!' };
// Request
const response = await server.post("/oauth2/signup").withCredentials().send(data);
// Status
expect(response.status).to.equal(400);
});
it("400 - 비밀번호에 대문자, 소문자, 숫자, 특수문자가 1개 이상 존재하지 않음", async function () {
// Data
const data = { email: 'signup1@rollback.com', password: 'test1234!' };
// Request
const response = await server.post("/oauth2/signup").withCredentials().send(data);
// Status
expect(response.status).to.equal(400);
});
})
});
테스트코드를 기반으로 원하는 결과가 나올 수 있도록 다음과 같이 로직을 작성해줍니다.
export const signUp = [
validate.json,
validate.email,
validate.password,
validateHandler,
async (req: Request, res: Response) => {
// Request Body Parameter
const { email, password, ...rest } = req.body;
try {
// Check - refresh_token 값은 로그인 성공시 쿠키로 저장되는 값입니다. 로그아웃시 지워집니다.
const refresh_token = req.signedCookies.refresh_token
if (refresh_token) {
console.log('BAD : 이미 로그인된 상태라서 로그아웃 이후 가입해주세요.', refresh_token);
return res.status(400).send(MESSAGE.ALREADY_LOGGED_IN);
}
// Check - user 데이터를 MongoDB에서 불러온 값이 있다면, 이미 있는 계정이라고 볼 수 있습니다.
let user = await User.findOne({ email });
if (user) {
console.log('BAD : 이미 존재하는 이메일을 입력했습니다.', email);
return res.status(409).send(MESSAGE.ALREADY_USER);
}
/**
* Task
* - 세션 아이디 생성 (세션 중복 로그인, 세션 유지시간)
* - 데이터베이스에 입력한 회원 데이터 저장
* - 회원가입 확인 메일 전송
*/
let session_string = randomBytes(32).toString("hex");
let newUser = new User({ email, password, session_string, ...rest });
try {
await newUser.save();
await sendSignUpConfirmationEmail(email, res);
res.status(201).send(MESSAGE.CONFIRM_EMAIL);
} catch (err) {
console.error('ERROR : 입력된 데이터가 데이터베이스에 저장되지 않았습니다.', err)
res.status(500).send(MESSAGE.TRY_AGAIN);
}
} catch (err) {
console.error('ERROR : 서버의 상태를 체크해주세요', err);
return res.status(500).send(MESSAGE.TRY_AGAIN); // 그 외에 에러나면 재요청 바람
}
}
];
[코드조각] db.ts (0) | 2020.11.17 |
---|---|
[코드조각] .env + config.ts (0) | 2020.11.17 |
[Node/Rest API] SFA - (1) - 인증 모듈과 사용자 모델 (0) | 2020.11.13 |
[Node/Rest API] API 리소스를 정의하는 3가지 요소 (0) | 2020.11.12 |
[Node/Rest API] MongoDB 스키마 만들기 (0) | 2020.11.11 |