Lsiron

2차 프로젝트 기간 백엔드 피드백 본문

회고록/회고록

2차 프로젝트 기간 백엔드 피드백

Lsiron 2024. 8. 12. 03:08

- 트랜잭션을 어떤 크기로 관리 할 것인지, 어떤 수준으로 컨트롤 할 것인지에 따라서 동시성 문제가 달라짐

- sql쿼리문으로 작성하는게 좋음 일단 prisma 같은 라이브러리는 사용하지 않는게 좋다.
=> prisma 사용하지말고 sql 쿼리문으로만 해보기

- 일반 회원가입을 빼고 구글 로그인으로만 하도록 구현하는게 좋음. 굳이 일반 회원가입과 일반 로그인을 넣지 않아도 됨.

- 유저테이블에 구글아이디 이렇게 넣지말고 컬럼에 타입(네이버)과 타입아이디(네이버아이디)만 추가할것

- 일반 로그인도 없애는 방향이 좋음. 소셜회원가입과 소셜로그인만 구현할 것
=> 구글이랑 네이버 소셜회원가입과 로그인만 추가

- user 테이블에 deleted_at 컬럼 추가

- 이메일과 이름 VARCHAR 타입에 자리 수 제한을 걸지 않는게 좋다.

- 유저아이디를 BIG INT로 할 것. 글로벌 회사에서 일반 INT 타입으로 하면 46억이 금방 넘어감. 아예 처음부터 BIG INT로 하는게 좋음. 만약 INT로 한다면 ,우리 서비스에서는 유저가 46억명이 넘어가지 않을 것이라 생각해서 INT라 했다. 로 대답. 46억이 넘어가면 DB가 멈춤. 왜냐하면 db 테이블에 락이 걸림. db타입의 컬럼을 바꾸려면 테이블을 먼저 락 걸어야함. 그러면 db 접근이 안됨. 그런데 자동으로 락 걸린 이후부터 1부터 46억번 까지 BIG INT로 데이터 타입을 바꾸기 시작하고 모두 바꿔야 락이 풀림.

=> 보통 테이블을 분리해서 기존 테이블이 BIG INT로 바뀔 때까지 다른 내부 테이블에서 INT로 쓰다가 기존 테이블이 BIG INT로 다 바뀌면 그때 기존 테이블로 변경 됨.

- 휴대폰인증도 db 테이블을 구성하는게 아니라 redis라는 데이터베이스를 사용하는게 좋음 휴대폰 인증만 redis 데이터베이스에 줄것. 차라리 할 것이라면 서버 세션에만 저장해도 충분함.

- 지역을 하나의 테이블에서 모두 관리할 것.

- 날씨 알림보다 notification 이라는 테이블 만들어서 타입으로 날씨와 관련된 타입이다. 로 만들 것. 그러면 다른 알림도 이 테이블에 넣어도 됨

--------------------------------------------------------------------------------------------------------------------------------------------

- 지역별 연 평균 aqi 수치와 실시간 aqi 수치를 비교하여 점수를 도출할 때 백엔드에서 구현을 어떻게 할 것인지?
=> 배치성 작업으로 계속 업데이트를 시키는 것 보단, 서버리스를 써서 구현하는게 좋을듯 하다. (서버리스가 돈이 안 나감). 
서버리스란 서버를 띄울 때만 로직이 돌아가도록 하고, 우리의 서버와 분리하는 것. (포트폴리오 사이트로 주로 활용) 

- 디커플링 개념.(외울것)
만약에 우리가 서버에서 노드 스케쥴러를 활용해서 1시간 마다 한번씩 내가 운영하는 서버에서 배치성 작업을 한다면, 서버에서 로직을 계속 실행하는 건데 여기서 문제는 만약 배치성 작업을 하고있는 서버가 장애가 나거나 비정상 종료가 될 수 있다. (특정 시간대마다 처리해야하는 로직이 처리가 안된다는 뜻) 서버가 장애가 나는 것 만으로 데이터 처리를 못 하면 문제가 생긴다.
 

디커플링이란, 꽉 묶여있는 것을 푼다고 하는 loose coupled라고 해서 이걸 디커플링 이라고 한다. 
하나의 서버에서 api도 처리하고 배치성 작업도 처리하기 때문에 하나가 문제가 생기면 전부 문제가 생긴다.
 허나, 서버리스가 실시간 작업을 다루게 하여 환경을 분리하면 하나가 문제가 생기더라도 모든 작업이 한 꺼번에 문제가 생기지 않는다.  이러면 적어도 우리 서버에선 장애가 나지 않게 할 수 있다. 의존성을 어떻게 분리하냐에 따라서 내 서버를 지킬 수 있느냐가 달라진다. 큐라는 시스템과 서버리스라는 개념을 공부 할 것. 

 

- 소셜로그인 => 1. 믿음으로 감 2. 인증서버만 따로 뺌 ( 여기서 서버리스라는 개념이 또 나옴. api gateway로 인증, 인가만 담당하는 서버만 따로 뺀 다음에 소셜로그인 장애가 발생하면,  큰 기업들은 세션을 사용하는 경우가 많이 있는데,  단, 쿠키를 쓸 땐 잘 안 뺌, 따로 빼면 외부서버에서 장애가 났을때 인가 처리는 jwt의 경우, jwt가 만료 됐더라도 일단 들여보낸다.  세션의 경우, 세션 로그인을 하더라도 쿠키값에 세션 아이디가 저장돼서 서버에 전달되기 때문에 세션이 만료가 될 경우에도 그냥 일단 들여보낸다. 여기서 문제는 처음 로그인 하는경우에 문제가 발생한다. -- 이건 대응할 수 있는 방법이 없다. 기존 로그인 하는 사람만 살려놓는 것. )

- 만약 서버리스로 한다면 aws lambda 서비스로 사용해야함. 백만번까지는 무료임. lambda를 활용해볼 것.

- 검색 최적화에 URL이 가장 중요함. main 엔드포인트를 빈 슬래시로 바꿀 것.

- Oauth가 왜 인가 프레임워크 인지 한번 생각 해 볼것. (면접)

- firebase auth 서비스와 aws의 코그니토 서비스를 쓰면 자동으로 구글 로그인을 구현 해 주는 것이 있다.

- 네이버로그인은 나중에 구현하고 일단 구글로그인으로 구현할 것. 차라리 구글로그인을 통해서 Oauth를 공부하는게 낫다.

- 마이페이지에서 응답 값에 user 테이블 필요없음 유저이름 수정할 때의 유저이름은 세션에 저장하면 됨. ( 어차피 세션으로 유저아이디와 유저이름을 조회 할 것이기 때문)


- 보통 마이페이지 할때 /my-page가 아니라 /my 라고만 함

- 닉네임 수정할 때 굳이 응답 값을 주지 않아도 됨. 204응답을 전달하는 경우가 많음.

- 회원탈퇴도 굳이 응답값을 전달해주지 않아도 됨. 굳이 백엔드에서 redirect 시켜주지 않고 클라이언트에서 redirect 시켜줄 것. 204응답.

- 사용자 관심 지역 조회 할 때 응답 값으로 유저아이디를 뺄 것. (세션에 있음) location_id 가 아니라 그냥 id로 넣을 것.

- 엔드포인트에서 변수값은 카멜케이스로 써도 됨. ex) :location_id

- 주요지역은 클라이언트에서 하드코딩 하고 api 호출을 안 하는 것이 좋음. 쓸데 없는 api 호출은 가능한 안 하는 것이 좋음.

- 그래프 페이지는 변수 값으로 이름을 적는게 아니라 id값을 넣을 것.(엔드포인트)

- 챌린지 데이터를 수정할 때 메서드는 일부만 수정한다면 PATCH를 사용할 것.(restful api)

--------------------------------------------------------------------------------------------------------------------------------------------

 

- Oauth는 인가 프레임워크이다
사용자는 인증을 구글서버에서 인증을 한다. 인증이 되면 구글서버에서 사용자한테 에어팡이란 서버에서 이름, 이메일, 핸드폰 번호를 사용하려고 하는데 이런 정보들을 에어팡이란 서버한테 전달해줄 의향이 있는지 물어본다.

그럼 사용자는 핸드폰 번호는 주기 싫은데? 하면 체크박스를 해제하고 동의하기를 누를 것이다. 그 때 어떤 정보를 전달해주냐면 req.query.code의 code를 백엔드로 전달해준다. 

 

서버에선 그럼 code를 전달 받은걸 구글서버에 물어본다. 구글서버야 혹시 나는 사용자로부터 code라는 정보를 받았는데 이 code안에 담긴 의미가 뭐야?
이러면 구글서버가 너가 준 코드를 분석해보니까 사용자는 언제 인증을 했고 인증을 할 때, 사용자의 이메일이나 이름을 너희 서버에 전달 해 주겠다고 표시를 했어. 그 정보를 한번 너가 알아봐 하면서 token을 준 것이다.

 

이 token은 구글 서버에 API를 요청할 수 있는 token을 준 것임. token을 가지고 있는 사용자만 API를 요청할 수 있다.
token은 구글서버에 API를 요청할 수 있는 권한이다. 그럼 구글서버는 이 token을 보고 사용자가 이름,이메일만 준다고 했다고 말해준다.

 

-  개인정보가 남아있지 않으면 db에 저장되는 로직을 짤 것.

 

- 현재 있는 token은 구글서버에서만 사용 가능한 token이다 우리 서버에서 사용할 수 있는 token을 만들어줘야한다.
그러면 클라이언트 입장에서는 token을 받았으니까 이제 백엔드서버로 API요청을 보낼 수 있겠구나 하고 알 수 있다.


- 현 코드의 res.send(tokens)로 토큰을 주면 브라우저의 로컬 스토리지나 세션에 저장되도록 했을 것이다. 허나 굳이 그럴필요는 없고 백엔드에서 쿠키를 만들어주면 된다.쿠키에 저장하면 클라이언트는 굳이 로컬 스토리지에 저장할 필요가 사라진다.


- 지금 추가 해야할 것은 쿠키를 발행하는 로직을 추가해야한다.

 

- 추천하는 것은 passport를 쓰는 것이다. 개발 면접때 Oauth, 회원가입, 로그인, 인증, 인가 이런것만 물어본다. 이걸 잘 답변하면 적어도 기본은 있는 사람이다.

 

- 네카라쿠배 같은경우 회원가입 로그인을 개발하라고 한달의 시간을 준다. 많은 기능을 개발할 필요는 없다. 하나의 기능을 깊게 개발하는 것이 포트폴리오 상 훨씬 좋다. 다양한 기능을 많이 개발했다는 것은 SI업체에서만 좋다. 
이 서비스 왜 했는지? 이 기술 왜 썼는지? 이 기술 장단점은 뭔지? 다른 기술이 더 좋은 점은 없는지? 지금 이 기술을 개발한다고 하면 어떻게 개발 할 것인지? 하나의 기능을 깊게 못하면 답변 하지 못 한다.
  
- Oauth 2.0을 왜 쓰는가? Oauth 1.0은 어떻길래 2.0을 쓰는가?

 

- scope 라는게 있다. Oauth1.0은 이 scope를 전달 할 수 없었다. Oauth1.0을 쓰면, 유저가 계정 권한을 주지 않았는데 서버에서 자유자재로 쓸 수 있었다. 구글 서버에서는 일단 구글 로그인이 됐으니까 권한을 다 풀어주는 것이다. 
즉, 세부적인 권한을 설정해주기위해서 2.0을 쓴 것이다.. = scope 지정(권한에 대한 범위 지정)

 

- Oauth2.0을 활용하는 건 알겠는데 어떤 방식으로 안전하게 쓸 지 생각해야한다. 

userPW 방식도 있고 clientID 방식도 있는데 왜 authorization 코드 방식이 가장 좋은 방식인지 의심해야한다.
예를 들어 지금 code를 query로 입력받았는데 query가 좋은지 body가 좋은지 생각해야한다. query로 받으면 API Url에 그대로 저장된다. 그럼 해커가 탈취 할 수 있는 가능성 존재한다. 이런 고민을 끝없이 해야하는데 이러한 고민에 대한 답만 내리면 면접은 문제 없다.

- 일단은 Oauth2.0과 OIDC 개념까지만 공부 해 볼 것. Oauth2.0이 산업업계 표준이니 2.1,  3.0 까진 나중에 알아보자.
=> 이런 흐름을 이해 해야한다.

- 로그인 기능 구현 한 다음에 할 것들.

1. 테스트코드를 작성할 것(TDD가 아님 - 아주 어려움) => 작성한 코드를 기반으로 정상적으로 작동하는지의 테스트 코드를 작성할 것. 테스트 코드를 작성하면 내가 작성한 비즈니스 로직이 잘못 작성했다는 기준을 확인 할 수 있다.
현업에서 괜찮은 개발문화를 가지고있는 곳은 테스트코드가 필수이다.

 

- jest라는 테스팅 라이브러리를 사용해 볼 것. 모카와 jest가 있는데 많은 팀이 jest를 사용한다. jest를 어떻게 사용하는지 공부 할 것.
 
2. 로그인 기능을 최대한 깊게 고민 해 볼 것. 어떻게 하면 개발을 잘 했다고 이야기 할 수 있는 가? 문제를 어떻게 해결했는가?

- 포폴을 어떻게 만들어야 할 지 모르는 사람이 많다.
1. 새로운 프로젝트를 개발할 필요는 없다.

 

2. 만약에 클론코딩을 한다면 일단 전부 클론 코딩을 하고 1차 결과물로 낼 것. 이 코드를 내 코드로 바꿔볼 것. 여기서 코드를 유지보수 하는 버전을 따로 만들것. 이 코드를 어떻게하면 잘 수정할 수 있는지를 배울 수 있다.  => 이러면 예를들어 카카오톡을 처음 클론코딩을 하면 유저가 10명밖에 이용을 못 할텐데, 점점 수정하면서 처음 수정은 1000명 두번째 수정은 10000명 이런식으로 점점 발전하는 것이 눈에 보이면 안 뽑을 수 없다.  -> 대신 시간이 오래걸린다.

 

3. 요즘은 이력서를 많이 낸다고 해서 뽑히는 게 아니다. 회사를 특정 지을 것.

 

4. 기술과제를 하더라도 이 기술을 배운다는 생각으로 제대로 할 것.

 

5. 프론트도 마찬가지다. 상태관리를 어떻게 할 것인가? 자바스크립트 동작방식을 명확하게 알고 있어야한다. 

 

6. 하루에 하나 씩이라도 개념들을 익혀볼 것. 남은 기간동안 js 동작방식만 이해해도 면접 때 말을 할 순 있다.

 

7. 책 한권 추천: core 자바스크립트 책. 백/프론트 둘 다 읽을 것. node쪽으로 간다면. 내가 사용하는 언어와 프레임워크가 어떻게 돌아가는지 알아야한다. 언제 쓰는지도 알아야한다. ex) 싱글스레드와 멀티스레드의 차이

 

8. vscode에서 디버그 모드를 실행해보면 코드가 어떻게 실행되는지 까볼 수 있다. 이걸 활용해볼 것.

 

9. 포폴을 굳이 배포할 필요는 없음. 코드가 대신 외부에 공개 돼 있어야함 ex) git hub. 이 코드를 보고 이 사람은 이 정도를 하는 사람이구나 파악할 수 있다. 

 

10. 할 수 있는 최선을 다해서 난 이렇게 코드를 잘 짜는 사람이라는 것을 보여줘야한다.

 

11. 나는 협업을 굉장히 잘 하는 사람인 것을 보여줘야한다. => 많은 개발팀이 우리는 이런 개발문화를 가지고있어요 하는 글이 있다. 이런 글을 보면서 풀 리퀘스트를 잘 생성하는법, 커밋메세지를 잘 작성하는법. 이런 규칙들을 하나 딱 잡고 얘는 어떻게 작성했냐를 따라 해 볼 것. 풀 리퀘스트를 어떤 식, 어떤 내용으로 작성하라는 기준이 있을것. 냅다 main/master에 커밋 올리면 안된다. 난 이 코드를 다른 개발자가 이해하기 쉽도록 문서화 했다 => 태도로써 보여줘야한다. 

 

12. 커밋메세지만 봐도 바로 알 수 있다. 나만 알 수있는 단어로 입력하는 것은 최악이다.

--------------------------------------------------------------------------------------------------------------------------------------------

 

- <controller의 역할>
보통 controller는 요청을 받아서 응답을 보낸다. 허나, 컨트롤러에 책임을 많이 부과하면 안된다. 이를 위해 직렬화와 역직렬화에 대해 알아야한다. 먼저, 역직렬화란? 정의를 내리긴 어려운 개념이다. 하지만 언제 사용하는지를 알면 정의를 내릴 수 있다. 이는 유효성 검증을 하는 로직에 사용된다. => DTO라는 것은 데이터 계층간 객체를 전달하는 것이다.

 

예를들어, 컨트롤러에서 서비스로 데이터를 전달할 때 우리는 보통 req객체를 바로 전달했다. 하지만 이러면 안된다.
컨트롤러에서 서비스로 데이터를 전달할 때 특정 객체를 전달해야한다는 이야기이다.

 

그럼 어떤 객체를 전달해야 하느냐?
1) 객체를 만들때 보통은 클래스의 인스턴스를 전달한다. 이를 위해 class-validator라는 라이브러리가 사용한다. 
그럼 결국 우리가 API요청을 전달할 때 어떻게 해야하나? 
직접 유효성검증 로직을 controller에 작성하는게 아니라 라이브러리를 활용해서 유효성을 검증하면 우리의 노고를 줄일 수 있다. 

 

여기서, 데코레이터를 활용하면 자동으로 유효성을 검증해준다.일단, DTO는 유효성 검증을 할 때 사용하는 녀석이다.
굳이 컨트롤러 서비스에 유효성 검증을 하는 로직을 작성할 필요가 없다.

 

2) DTO의 역할은 무엇인가?
유효성 타입을 변환해주는 역할을 하고있다.

 

3) DTO는 어떤 책임이 있는가?
타입 변환, 유효성 검증, 데이터 조합 책임이 있다. 즉,  이 말은 컨트롤러에서 직접 해 줄 필요가 없다는 것 이다. => 캡슐화, 추상화
req.user 이런식으로 짜는것 보다 DTO 개념을 사용하는것이 기본이다. 이것을 지키지않으면 기술과제에서 불리하다.

- 그러면 역직렬화란 무엇인가? 
보통 요청이 JSON으로 들어오는데 이 JSON 데이터형식을 class의 인스턴스로 변환해주는 것이다.

 

- 그렇다면 직렬화란?
반대로 class의 인스턴스를 JSON으로 변환시켜주는 것이다.

- 그렇다면 직렬화는 왜 필요한가?
보통 우리가 코드를 짤 때 repository 같은 곳에서 데이터를 받아와서 가공한 뒤, 서비스로 넘겨주고 이를 컨트롤러로 넘겨서 컨트롤러에서 응답을 넘겨줬을 것이다. 허나 이렇게 코드를 짰을 때 db의 컬럼이름이 변경된다고 가정해보자. 그러면 우리의 코드는 api응답 값이 유지되지 않는다.

- 컬럼 이름이 변경되더라도 api 응답값이 바뀌면 안된다. 이러면 유지보수가 굉장히 어렵게 된다. 결국 db하나 바뀌는 것만으로 백엔드 부터 시작해서 프론트까지 먹통이 된다. 

- 이 변화의 흐름을 막아야한다. 여기서 바로 직렬화의 개념을 써야한다.
그렇다면 어떻게 하느냐. 보통 계층간 데이터를 전달해 줄 때 하나의 객체를 쓴다고 가정해보자. 그렇다면 db는 보통 모델 혹은 repository에서 받아온 데이터를 서비스에 전달해주는데 이 때 객체를 전달해준다.

만약 우리가 is_name을 받아온다면 is_name을 바로 전달 해 주는 것이 아니라 is_name => isName으로 바꿔서 전달해야한다. 즉, 컬럼명을 변수로 받아와서 우리만의 isName으로 바꿔줘야 하는 것.

이러면 repository라는 공간과 DTO라는 공간에서만 데이터가 바뀌게 되고 앞단에 있던 코드의 변화가 생겨도 뒤에는 영향을 받지 않는다. 

보통 유저 db에서 데이터를 가져올때 findAll()이런식으로 데이터를 다 가져왔을 것이다. 이러면 만약 유저정보에서 이메일만을 가져와야하는데 다른 정보인 비밀번호까지 가져오게 돼서 응답 값으로 필요없는 비밀번호까지 주는 것이다.

하지만 DTO는 외부에 전달되면 안되는 코드들을 제거한다.

이러면 서비스계층으로 불필요한 코드를 전달하지 않게 되고 보안 측면에서도 기능이 향상된다.