저장을 습관화
에러 기록 - "서비스에서는 컨트롤러가 필요한 형태로 값을 반환해야 합니다." 본문
- 증상
로그인 API를 만들고, 정상적인 email과 password를 입력하니
코드의 실행은 정상적으로 진행되어 200코드가 나왔으나,
토큰값이 undefined로 생성됨
- 원인
userController.js
const UserService = require('../services/user_service');
class UsersController {
userService = new UserService();
// (생략)
// 로그인 API
logIn = async (req, res) => {
const {email, password} = req.body;
try {
const {token} = await this.userService.login(email, password);
// service로 인자를 전달하고 있는 controller에서의 변수 token은 객체(object)이다
res.cookie('authorization', `Bearer ${token}`);
return res.status(200).json({message: '로그인에 성공하였습니다.'});
} catch (error) {
return res.status(400).json({errorMessage: error.message});
}
};
}
module.exports = UsersController;
service로 인자를 전달하고 있는 controller에서의 변수 token은 객체(object)이다
userService.js
const UserRepository = require('../repositories/user_repository');
const jwt = require('jsonwebtoken');
class UserService {
userRepository = new UserRepository();
// (생략)
// 로그인
login = async (email, password) => {
const user = await this.userRepository.findUserByEmail(email);
// (생략)
try {
const token = jwt.sign(
{
userId: user.userId,
},
'customized_secret_key',
);
return token;
// 하지만 service에서 controllers로 리턴하는 변수 token은 문자열(string)이다.
} catch (error) {
throw new Error(error);
}
};
}
module.exports = UserService;
하지만 service에서 controllers로 리턴하는 변수 token은 문자열(string)이다.
즉 controller에서는 service의 실행 결과를 객체 타입인 변수 token에 할당하겠다고 선언했으나
service에서는 문자열의 결과를 리턴해서 발생한 문제
route 파일에서 모두 처리하던 로직을 3계층 아키텍쳐로 나누다보니 발생한 듯 하다..
- 해결
userController.js
// ...
const token = await this.userService.login(email, password);
// ...
controller에서 변수 token의 중괄호를 제거했다.
정상적으로 토큰이 생성되었다.
이 부분은 chatGTP에게 물어봤다.
나: 이 부분에 대해서 좀 더 자세히 설명해줄수있어? 컨트롤러에서는 객체(object)로 선언했는데 서비스에서는 문자열(string)으로 리턴해서 그런거야?
chatGPT: 물론이죠, 그렇게 이해하셔도 됩니다. 상황을 더 자세히 설명해보겠습니다.
서비스에서 `jwt.sign()` 함수를 사용하여 JWT 토큰을 생성할 때, 이 함수는 생성된 토큰을 문자열 형태로 반환합니다. 여기서 반환되는 값은 단순한 문자열입니다. 따라서 서비스에서 반환하는 것은 단순한 문자열인 토큰 값입니다.
반면에 컨트롤러에서는 서비스를 호출한 결과를 객체 형태로 분해하여 사용하고자 합니다. 그래서 컨트롤러에서 `const { token }` 형태로 분해하려면, 서비스에서도 객체의 속성으로 `token`을 포함한 객체를 반환해야 합니다.
예를 들어, 서비스에서 다음과 같이 `{ token }` 형태의 객체를 반환하면:
```javascript
return { token };
```
그럼 컨트롤러에서 `const { token }`을 사용하여 분해할 수 있게 됩니다.
반면에 서비스에서 단순히 `token` 값을 문자열로 반환하면:
```javascript
return token;
```
이 경우 컨트롤러에서 `const { token }`을 사용해도 분해할 객체가 없어서 `token`은 `undefined`가 됩니다.
따라서 서비스에서는 컨트롤러가 필요한 형태로 값을 반환해야 합니다. 객체 형태로 분해하고자 하는 변수를 해당 객체에 속성으로 포함시켜 반환하면 됩니다.
얘가 한 말 중 "서비스에서는 컨트롤러가 필요한 형태로 값을 반환해야 합니다." 라는 말에 머리가 띵 하더라
아 또 클래스의 이해가 부족했구나 그래서 object Object 에러가 나온거로구나 싶었다
controller와 service 모두 string일 때 정상적으로 실행되는 것은 확인 했으니,
테스트 1. controller에서의 token은 object이나, service에서 반환하는 token은 string일 때 (오늘 에러 상황)
userController.js
// ...
const {token} = await this.userService.login(email, password);
console.log(token); // undefined
console.log(typeof token); // undefined
res.cookie('authorization', `Bearer ${token}`);
return res.status(200).json({message: '로그인에 성공하였습니다.'});
// ...
userService.js
// ...
const token = jwt.sign(
{
userId: user.userId,
},
'customized_secret_key',
);
console.log(token); // eyJhbGciOi.....
console.log(typeof token); // string
return token; // string인 token을 반환
// ...
결과 - 에러
테스트 2. controller에서의 token은 string이나, service에서 반환하는 token은 object일 때
userController.js
// ...
const token = await this.userService.login(email, password);
console.log(token); // {token: 'eyJhbGciOi...(생략)'}
console.log(typeof token); // object
res.cookie('authorization', `Bearer ${token}`);
return res.status(200).json({message: '로그인에 성공하였습니다.'});
// ...
userService.js
// ...
const token = jwt.sign(
{
userId: user.userId,
},
'customized_secret_key',
);
console.log(token); // eyJhbGciOi.....
console.log(typeof token); // string
return {token}; // string인 token을 object로써 반환
// ...
결과 - 에러
토큰이 Bearer%20%5Bobject%20Object%5D 라고 생성되었다.
표현식 제거하고 보면 object Object 에러이다.
테스트 3. controller와 service 모두 객체(object)일 때
userController.js
// ...
const {token} = await this.userService.login(email, password);
console.log(token); // eyJhbGciOi.....
console.log(typeof token); // string
res.cookie('authorization', `Bearer ${token}`);
return res.status(200).json({message: '로그인에 성공하였습니다.'});
// ...
userService.js
// ...
const token = jwt.sign(
{
userId: user.userId,
},
'customized_secret_key',
);
console.log(token); // eyJhbGciOi.....
console.log(typeof token); // string
return {token}; // string인 token을 object로써 반환
// ...
결과 - 정상
리포지터리에서는 서비스가 요청한 타입의 데이터를 반환하여야 하고,
서비스에서는 컨트롤러가 요청한 타입의 데이터를 반환하여야 한다.
타입스크립트를 사용해야할 이유를 하나 더 느꼈다
'공부 > node.js' 카테고리의 다른 글
express - 미들웨어 작성 기초 (0) | 2023.09.29 |
---|---|
Nest.JS로 API 만들기 - 1 (0) | 2023.09.06 |
에러 기록 - [object Object] (0) | 2023.08.08 |
Nest.js 연습 - 게시판 만들기 2 (0) | 2023.08.05 |
Nest.js 연습 - 게시판 만들기 (0) | 2023.08.04 |