[TIL] 내일배움캠프 1.4

Asher Park·2023년 1월 13일
0

내일배움캠프_TIL

목록 보기
25/39
post-thumbnail

심화주차 프로젝트를 하면서 Error Handling에 대한 고민을 정말 많이하게 되었다.

3-Layered Architecture Pattern을 적용하기 전에는 하나의 Router 안에서

비동기 작업에 대해 Promise 방식이나 try-catch 방식으로 에러 핸들링을 해 왔었다.

router.get("/posts", async (req, res, next) => {
	try {
     	...
    } catch (err) {
    	...
    }
}

그러면 3계층 아키텍처 패턴에서는 어떤 방식으로 에러 핸들링을 해줘야할까?

기존의 하나의 Router 안에서 해오던 비즈니스 로직을 Controller, Service, Repository 로 나누어 지니까 모든 계층에서 에러가 날 수 있다고 생각이 되었다.

Controller 계층의 주 동작은 클라이언트에서 넘어오는 Data 들의 유효성 검사, 그리고 서버에서 클라이언트로 데이터를 다시 뿌려주는 역할이다.

그렇기 때문에 아래 계층에서 일어난 에러들을 Controller 계층까지 보내서 한번에 클라이언트에게 뿌려주는 방식이 맞다고 생각되었다.

모든 계층에서 try-catch를 쓰고, 발생하는 에러의 status code와 message, 그리고 name을 정의 한 후, throw 하는 방식으로 작성하였다.

  • Repository
    - Repository 계층에서는 DB에 접근하는 기능들의 에러를 throw 해준다.
	...
    createUser = async (id, name, password) => {
      try {
      	await this.customerModel.create({ id, name, password });
        return { status: 200, success: true, message: '회원가입에 성공하였습니다.' };
      } catch (error) {
      	error.name = 'Database Error';
        error.message = '요청을 처리하지 못하였습니다.';
        error.status = 400;
        throw error;
      }
    };
  • Service
    - Service 계층은 주요 비즈니스 로직이 구현되어 있고 발생하는 에러들을 throw 해준다.
	...
    createUser = async (id, name, password) => {
        try {
            // 해당 id를 가진 유저가 이미 존재하는지
            const existUser = await this.customerRepository.isUserExist(id);
            // 존재한다면?
            if (existUser[0].length !== 0 || existUser[1].length !== 0) {
                const error = new Error('이미 사용중인 아이디입니다.');
                error.name = 'Already User Existed';
                error.status = 400;
                throw error;
            }

            // 비밀번호 암호화
            const hashedPassword = await bcrypt.hash(password, 10);

            // 회원가입 진행
            return await this.customerRepository.createUser(id, name, hashedPassword);
        } catch (error) {
            throw error;
        }
    };    

하위 계층에서 throw error로 에러를 반환하면 계속 catch 문으로 빠지게 되고,
최상위 계층인 Controller 계층의 catch 에서 에러 핸들링 하게 된다.

...
// 고객 회원가입
    signUp = async (req, res, next) => {
        try {
            const { id, name, password, passwordCheck } = await customerRegisterValidateSchema.validateAsync(req.body);

            const signUpResult = await this.customerService.createUser(id, name, password);

            return res.status(signUpResult.status).json({ success: signUpResult.success, message: signUpResult.message });
        } catch (error) {
            // Joi Error
            if (error.name === 'ValidationError') {
                error.status = 412;
                error.success = false;
                switch (error.details[0].path[0]) {
                    case 'id':
                        if (error.details[0].type === 'string.empty') {
                            error.message = '아이디를 입력해주세요.';
                            break;
                        }
                        error.message = '아이디의 형식이 일치하지 않습니다.';
                        break;
                    case 'name':
                        if (error.details[0].type === 'string.empty') {
                            error.message = '이름을 입력해주세요.';
                            break;
                        }
                        error.message = '이름의 형식이 일치하지 않습니다.';
                        break;
                    case 'password':
                        if (error.details[0].type === 'string.empty') {
                            error.message = '비밀번호를 입력해주세요.';
                            break;
                        }
                        error.message = '비밀번호 형식 일치하지 않습니다.';
                        break;
                    case 'passwordCheck':
                        if (error.details[0].type === 'string.empty') {
                            error.message = '비밀번호 확인을 입력해주세요.';
                            break;
                        }
                        error.message = '비밀번호가 일치하지 않습니다.';
                        break;
                    default:
                        break;
                }
            }
            return res.status(error.status).json({ success: error.success, message: error.message });
        }
    };

Controller 계층의 catch 안에서 클라이언트에게 에러를 전달하게 된다.

profile
배움에는 끝이없다

0개의 댓글