일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |
- flask
- TypeScript
- Python
- 게임
- nestjs
- Sequelize
- cookie
- MySQL
- AWS
- typeORM
- Nest.js
- 자료구조
- Queue
- OCR
- GIT
- Express
- jest
- game
- class
- react
- 정렬
- MongoDB
- mongoose
- 공룡게임
- dfs
- nodejs
- JavaScript
- Dinosaur
- Bull
- Today
- Total
포시코딩
1월27일 - 궁금증 해결 본문
오늘은 묵혀놓은 궁금한 것들을 해결하는 시간을 가져보았다.
구글링 및 그동안 공부한 것들 종합 + 튜터님한테 직접 물어보는 방법을 통해
아래 다섯가지 상황과 결과를 정리해보았다.
api 통합 vs 분리
개요
- 대기중인요청
- 진행중인 요청
- 완료된 요청
위 세가지 요청 리스트 api에 대해
정리하던중 다시 path를 깔끔하게 재할당 해줘야하는 상황이 생겼는데
두가지 선택사항이 생겼다.
방법 찾기
1. 라우터에서 부터 나눠지게?
/api/orders/waiting?p=1
/api/orders/doing
/api/orders/done?p=1
2. GET /api/orders로 들어가 컨트롤러에서 type에 따라 다른 service 호출?
/api/orders?type=waiting&p=1
/api/orders?type=doing
/api/orders?type=done&p=1
두 방법의 차이를 정리해봤다.
방법 1.
장점: 라우터부터 분리되어 처리되므로 직관적이고 코드가 매우 깔끔해진다.
단점: 엔드포인트가 많아진다는 점
api의 개수가 많아지며 만약 waiting, doing, done 3가지가 아니라 더 많을 경우 다 각각 구현해야함
방법 2.
장점: api에 대해 하나만 사용해도됨.
더 추가되더라도 type에 대한 query parameter를 받아 처리해주면 문제 없음
단점: 각각의 type에 대한 로직, 예외처리가 달라지는 과정에서 어려움을 겪을 수 있다.
결론
뭘 선택해도 정답인 느낌이라 튜터님의 도움을 받았다.
대답은 어차피 지금 상황에서 3개 이후로 안늘어나는거면 1번의 방법을 추천하셨는데
10개 이상으로 많은 상황에서도 최대한 1번의 방법을 사용하되
query parameter로 구분할 수 있는건 2번 방법으로 받아들여
typescript의 type으로 필터링 하여
const getOrders = (filter: Filter) => {
// filter -> 안에는 query parameter 넣어서
}
이런식으로 처리하는 방법을 추천해주셨다.
아직 TypeScript를 써서 본격적으로 프로젝트를 만들어보질 않아
정확한 예시 코드가 떠오르진 않지만 대충 어떤 느낌인진 알 것 같다.
해결!
service -> repository 호출 방법 고민
1. 여러 개의 repo를 부르는 방법
const { User, Token, Order } = require('../sequelize/models');
class UsersService {
usersRepository = new UsersRepository(User);
tokensRepository = new TokensRepository(Token);
ordersRepository = new OrdersRepository(Order);
2. 한 개의 repo에 여러 개의 model을 넣는 방법
const { Order, User } = require('../sequelize/models');
class OrdersService {
ordersRepository = new OrdersRepository(Order, User);
1번의 고민과 마찬가지로 취향차이 문제.
위 두 가지 방법 다 잘 작동하는데
앞으로 있을 프로젝트에서 어떤 방법을 사용할지 결정이 안난 상태였다.
애초에 나는 1번의 방법으로 전 프로젝트를 구성했었는데
생각보다 많은 팀이 2번의 방법으로 프로젝트를 구현해서 더 고민이 된 문제였음
1번의 경우엔 해당 service를 호출할 때 수많은 repo 인스턴스를 만든다는게 걸렸고
2번의 경우엔 해당 repo의 이름과 맞지 않는 메소드가 그 안에 위치하고 있는 경우가 생긴다는게 걸렸는데
결국 1번의 방법으로 진행하기로 했다.
이유는, 우선 repo 하나에는 두개 이상의 model이 들어가면 안된다는게 요지였고
무엇보다 2번을 고려했을 때 걸렸던 성격과 맞지 않는 메소드가 자리 잡는다는게 결정에 영향을 미쳤다.
new ~ 로 새 인스턴스를 만드는게 불편한건
어차피 추후 Nest.js를 배우게 되면 모두 해결될 문제니 당장은 참는 것으로 결론.
해결!
파일명 고민
(트리구조)
위 두 같은 구조에 대해 파일명을 어떻게 해야할지에 대한 고민이었는데
그냥 바로 .controller, .service를 붙이는걸로 결정했다.
이유는 다음과 같다.
- 종류가 더 많아질수록 구분하기 용이해짐
- 파일명 길면 어쩔거임
- 확장자와 비슷한 역할로 받아들일 수 있다.
실제로 bash를 써서 개발한다면 파일 찾을 때 도움 될듯 - 굳이 안 쓸 이유가 없다.
해결!
쿠키 생성 주체
https://github.com/9hezo/save_my_keyboard/issues/31
이전 프로젝트에서 지적 받았던 피드백이다.
요즘엔 거의 다 프론트엔드 서버 - 백엔드 서버가 분리되기도 하고
애초에 app에서는 쿠키 자체가 존재하지 않아
서버에서 res.cookie를 통해 쿠키 설정한다 해도 로그인 유지가 되지 않는다. (폰 유저 포기할거임?)
app에다가도 걍 res.json으로 만든 토큰 던져줘서 알아서 세팅하게끔 하자라는 느낌
before
controller
res.cookie('accessToken', response.accessToken);
res.cookie('refreshToken', response.refreshToken);
after
controller
return res.status(response.code).json({
accessToken: response.accessToken,
refreshToken: response.refreshToken,
});
login.js
fetch('/api/users/login', {
method: 'POST',
// ...생략
.then(async (res) => {
// ...생략
document.cookie = `accessToken=${res.accessToken}`;
document.cookie = `refreshToken=${res.refreshToken}`;
})
위 피드백을 받은 후 변경한 모습인데 여기까진 잘했으나 한가지 문제가 있었다.
authMiddleware
if (accessToken인증실패) {
// ...생략 (대충 refreshToken으로 저장해놓은 userId 가져오는 로직)
const newAccessToken = await TokenManager.createAccessToken(tokenInfo.userId);
// 새롭게 발급받은 accessToken 세팅 필요
// 클라이언트에서 저장하게 변경되어야함
res.cookie('accessToken', newAccessToken);
}
어떤 요청에 대해 로그인 확인을 할 때 accessToken만 만료됐을 경우
refreshToken을 통해 다시 재발급받으며
자연스럽게 해당 요청을 수행하는데
이 과정에서 accessToken 세팅하는 부분이 아직 서버에서 직접 설정해주고 있는 상태였다.
이런 경우 어떻게 해야하나 궁금해서 튜터님한테 조언을 구했는데
그냥 이것도 해당 요청을 들어주지 말고 일단 accessToken을 리턴시키고
프론트엔드에서 받아서 로그인 처리를 먼저 하게끔 유도하는게 맞다고 하셨다.
그럴경우 내가 만약 프론트엔드라면 요청했는데 accessToken을 응답받으면
다시 저장한다음 재요청을 하여
사용자 입장에서는 자연스러운 흐름이 되도록 할 것 같았다.
근데 이것도 개발자나, 회사마다 다 방법이 달라서 내가 생각한 방법도 맞고
진짜 여러가지 방법이 있을거라고 하심
어쨋든 결과적으로는 그냥 서버에서 처리할 생각 하나도 하지말고
token은 다 넘겨버려라! 가 결론
해결!
status 관리
위 status 값에 대해
아래와 같은 피드백을 받았다.
https://github.com/9hezo/save_my_keyboard/issues/33
처음에는 db에 저장되는 값은 괜찮지만
코드로서 개발자가 봤을 때 가독성이 좋지 않아 발생하는 문제로 인식해서
최근에 배우는 TypeScript의 enum을 통해 해결할 수 있다고 생각했다.
실제로
enum Status {
waiting,
collected,
collecting,
delivering,
delivered,
cancelled
}
let status1: Status = Status.waiting;
let status2: Status = Status.collecting;
console.log('status1: ' + status1); // 0
console.log('status2: ' + status2); // 2
위 코드를 통해 db와 api문서 수정 없이 해결할 수 있을거라고 봤는데
물론 이 방법도 괜찮은 방법이지만
튜터님이 말하고자 했던건
이제는 여러 성능이 받쳐주기 때문에 굳이 status 값을 tinyint와 같은 값으로 사용하지 않고
그냥 'waiting', 'cancelled' 같은 varchar를 써도 된다는 입장이었다.
예로
어떤 문제가 발생해서 디비상에서 직접 특정 status의 사용자들에 대해
어떤 값을 수정해줘야 한다는 상황이 발생했을 때
디비상에서 status 숫자만 막 0, 1, 4, 2, ... 이런식일텐데
어떻게 하나하나 다 구별할거임? 이거 다 외울거냐고
그리고 만에 하나 외웠다고 생각해서 쿼리문 써서 딱 엔터 눌렀는데
사실 그게 아니었다면? 대형 사고가 나는 것이다.
이런 휴먼 에러를 방지하는게
그 용량 조금 때문에 고민하는거보다 훨씬 낫다는 의견을 주셨다.
물론 위의 얘기들은 모두 다 개인 또는 회사의 생각 차이가 있을 수 있다는 점을 같이 얘기 하셨는데
내 경험상으로도 실제로 디비상에서 직접 값을 수정하는 일들이 가끔씩 있었던 경험을 생각해봤을 때
틀린 말이 아니었다.
때문에 당장은 status를 varchar로 바꾸는건 너무 큰 공사라
이번만 숫자값으로 사용하고
다음 프로젝트들에서는 varchar값을 사용해보는걸로 결론을 지었다.
해결!
'TIL' 카테고리의 다른 글
1월29일 - Test Code 작성하면서 궁금한거 정리 (0) | 2023.01.29 |
---|---|
1월28일 - cookie와 path 설정 (0) | 2023.01.29 |
1월26일 - console.log 자동완성, 객체 구조 분해 할당 재작명 (3) | 2023.01.26 |
1월25일 - WebSocket -> socket.io 변경을 하며 (0) | 2023.01.25 |
1월24일 - DB time zone (0) | 2023.01.24 |