Wecode - Project 3 (부트캠프)/Project 3 독학

에러 핸들링 3 [각 Layer 별 Error Handling] 개념 + 실제

Queen Julia 2023. 10. 27. 13:23

각 레이어 별 에러

Controller - Service - Dao
각 레이어 별로 각기 다른 목적을 위한 에러 핸들링

에러가 발생하는 위치와
실제 에러 메세지를 던지는 위치가 실제와 상이할 수 

각 레이어별 에러 핸들링 시 동기, 비동기적 에러 처리에 유의하여 코드를 작성

1. Controller Error

입력과 출력에 관련

 

HTTP 통신을 처리하는 웹 서버의 관점에서는

http request (요청 입력), http response (응답 출력)을 담당하는 레이어

 

따라서 통신이 처음 시작될 때,

사용자로 부터 받은 정보를 클라이언트(프론트엔드)가 전달해주면,

 

해당 입력에 대한 유효성 검증을 처리하며

에러 및 예외가 발생


KEY_ERROR가 대표적

console.log(req.body) // { password : 'myPassword' } 

const { email, password } = req.body

console.log(email) // undefined
console.log(password) // 'myPassword'

if (!email) {
  throw new Error('KEY_ERROR')
} // 에러 발생 시점

 

if() { throw new Error (' ') }

 

case1. (키가 안 들어온)

  • email을 console로 찍으면 아예 undefined로 들어옴 

 

기타 가능한 상황 

  • 회원가입 시 email, password 등 필수 필요 정보가 누락되었을 때
  • 주문 생성 시 주문하는 제품에 대한 필수 정보 product_id, quantity, user_id 등이 누락되었을 때

if ( ) {

const err = new Error(' ')

err.statusCode = 400;

throw err

}   

 

// 사용자가 입력 안 한 거면, 주로 400번대 http status code

 

 

case2. (key는 있지만 해당하는 키에 빈 값이 들어오는)

  • email을 console로 찍은 부분이 ' ' 비어서 들어옴 
// 키에러가 발생하지 않으나, 값이 빈 상태

console.log(req.body) // { email: '', password: 'myPassword' }

const { email, password } = req.body

console.log(email)    // ''
console.log(password) // 'myPassword''

if (!email) {
	const err = new Error('KEY_ERROR')
	err.statusCode = 400;
  throw err
} // 에러 발생하지 않음.

2. Service Error

주로 비즈니스 로직, 규칙을 설정

 

 보통 기획에 따른 비즈니스 규칙에서 비롯된,

개발자가 직접 의도한 예외와 관련된 에러가 발생

 

에러의 발생 위치는

Service 단 하위에 위치한 계층의 모듈과,

직접 이곳에서 호출하는 서비스 모듈의 에러가 발생

 

만일 비즈니스 규칙에 위반되는 사항이 발생가능하다면

꼭 이곳에서 필히 해당 문제의 경우에 상응한 Status Code와 함께 에러를 발생

  • 고객 입력 문제 라면 400번대 Status Code
  • 내부 처리 과정 중 문제라면 500번대 Status Code

ex. [회원 가입]

서비스 단에서 기획에 맞게 꼭 설정되어야 하는

비밀번호의 보안성과 관련하여

올바른 형태의 비밀번호를 적었는지 그 유효성을 처리하는 모습

 

if ( ) {

const err = new Error (' ');

err.statusCode = 400

throw err;

}

//service/userService.js

const userDao = require('../models/userDao')

// 정규 표현식을 이용한 비밀번호 유효성 검증
const signUp = async (name, email, password, profileImage) => {
    const pwValidation = new RegExp(
      '^(?=.*[A-Za-z])(?=.*[0-9])(?=.*[!@#$%^&*])(?=.{8,20})'
    );
    if (!pwValidation.test(password)) {
      const err = new Error('PASSWORD_IS_NOT_VALID');
      err.statusCode = 409;
      throw err;
    }
      const createUser = await userDao.createUser(
          name,
          email,
          password,
          profileImage
        );
      
        return createUser;
      };
  
  module.exports = {
      signUp
  }

3. Dao Error

데이터 액세스 계층

: 주로 영속적인 저장소와 관련된 코드를 작성하는 곳 

 

주로 데이터 베이스 드라이버가 위치하는 곳 -->  데이터 베이스에 의존적인 에러가 발생

 

Node + Express와는 다른 외부 시스템으로서 연동,

이 때 주로 비동기 호출을 하기 때문에,

비동기 에러 핸들링에 적합한 방법을 이용해야 

 

데이터베이스의 문제로 에러가 발생한다면 500번대 status code 

나머지 예외적인 사항(데이터 없음, 이상 현상)에서는 적절한 status code 및 에러

const { DataSource } = require('typeorm');

const myDataSource = new DataSource({
	type: process.env.TYPEORM_CONNECTION,
    host: process.env.TYPEORM_HOST,
    port: process.env.TYPEORM_PORT,
    username: process.env.TYPEORM_USERNAME,
    password: process.env.TYPEORM_PASSWORD,
    database: process.env.TYPEORM_DATABASE
})

myDataSource.initialize()
	.then(() => {
    console.log("Data Source has been initialized!");
  })
  .catch((err) => {
    console.error("Error during Data Source initialization", err);
    myDatasource.destroy()
  });

const createUser = async ( name, email, password, profileImage ) => {
	try {
		return await myDataSource.query(
		`INSERT INTO users(
			name,
			email,
			profile_image,
			password
		) VALUES (?, ?, ?, ?);
		`,
		[ name, email, password, profileImage ]
	  );
	}
	catch (err) {
    const err = new Error('INVALID_DATA_INPUT');
    err.statusCode = 500;
    throw err;
	}
};

module.exports = {
  createUser
}

 

const 함수명 = async ( ) => { 

try { 

         await myDataSource.query ( ) 

        } catch (err) {

             const err = new Error (' ');

              err.statusCode = 500;

         throw err;

          }

      };


개념은 이러한데, 실상은 

 

 

try, catch는 controller에서 했다. 

 

 

 

[controller.js ]

 

const 함수명 = async (req, res) => {

      try { 

         return res.status(200).json({message: 'Success'});

      } catch (error) {

         console.log(error); // 이렇게 해야 에러 났을 때 터미널에 찍힌다

        res.status(error.status || 500).json ({message: error.message}); 

     }

  } 


[Service.js ]


const { throwError } = require( '../utils/throwError' );   //상단에서 throwError 불러오기 (import, require)

 

const 함수명 = async ( ) => {

            if ( ) throwError (400, '    ');    //서비스 기획 단에서의 '에러 핸들링': 예외처리

 

           if ( ) throwError (400, '    ');    //서비스 기획 단에서의 '에러 핸들링': 예외처리

  } 

 

 

[Dao.js]

데이터베이스 관련 데이터 넣고 수정 삭제 관련 일이라

에러 관련은 처리 안 한다.