Skip to content

Conversation

@jeong724
Copy link
Collaborator

@jeong724 jeong724 commented Jun 25, 2025

📝 Work Description

⭐️ 필수 과제

  • 댓글 기능 추가
  • 좋아요 기능 추가
  • 페이지네이션 적용

🥇 선택 과제

  • 외부 API 연동
  • 검색 기능 (QueryDSL 기반)
  • 태그 기능 확장
  • 대용량 데이터 처리 및 정렬 최적화
  • 병렬 처리 & 비동기화
  • 캐싱으로 성능 최적화
  • 인프라 설계 시각화
  • JWT 발급 및 검증 로직 구현
  • 로그인 API 리팩터링

💡 키워드 과제

  • 리버스 프록시란 무엇인가요 ?
  • 리버스 프록시는 클라이언트의 요청을 받아 대신 실제 서버로 전달하고, 응답을 다시 클라이언트에게 전달하는 중간 역할을 하는 서버이다. 사용자가 직접 백엔드 서버에 접속하는 것이 아니라, 먼저 리버스 프록시를 거쳐가도록 구성된다.
    그럼 왜 리버스 프록시를 사용할까?
  1. 로드 밸런싱
    트래픽이 몰릴 경우 하나의 서버만 사용하는 것이 아니라 여러 서버에 요청을 분산시켜 서버의 부담을 줄이고, 서비스가 끊기지 않도록 유지할 수 있다.
  2. 보안적인 측면
    클라이언트는 실제 서버의 위치를 알 수 없고, 모든 요청은 프록시 서버를 통해 들어오기 때문에 외부 공격으로부터 내부 서버를 보호할 수 있다.
  3. 정적 파일 캐싱 기능
    HTML, CSS, 이미지 등 자주 바뀌지 않는 파일은 프록시 서버가 미리 저장해두었다가 빠르게 응답해줄 수 있어 전체 서비스 속도가 빨라진다.
    => Nginx, Apache, Cloudflare, AWS Load Balancer 등 다양한 도구들이 리버스 프록시 역할을 한다.
  • 쿠키 vs 웹 스토리지 간단히
  • 쿠키와 웹 스토리지는 모두 브라우저에 데이터를 저장할때 쓰임 but 용도와 동작 방식에 차이가 있다.
    쿠키는 주로 서버와 통신할 때 필요한 정보를 저장하는 데 사용된다. 브라우저가 서버에 요청을 보낼 때마다 쿠키에 저장된 데이터도 함께 전송되기 때문에, 로그인 상태 유지나 사용자 인증에 자주 사용된다. 다만 저장 용량이 작고, 서버로 매번 전송되기 때문에 속도와 보안 측면에서 주의가 필요하다.
    웹 스토리지는 클라이언트에서 데이터를 저장하는 공간으로, 서버와는 자동으로 데이터를 주고받지 않는다. localStorage는 데이터를 브라우저에 영구적으로 저장하고, sessionStorage는 브라우저 탭을 닫을 때까지만 유지된다. 용량도 크고, 속도도 빠르기 때문에 프론트엔드에서 임시 데이터나 사용자 설정 값을 저장하는 데 자주 쓰인다.
  • OAuth 란 무엇이고 어떤 이유에 의해 필요하게 되었을까요 ?
  • OAuth는 외부 서비스에 내 계정 권한의 일부를 안전하게 위임할 수 있게 해주는 인증 방식이다.
    과거에는, 어떤 서비스를 이용하려면 직접 내 계정의 아이디와 비밀번호를 입력해야 했는데.. 보안상 위험하기 때문에
    OAuth를 사용해서 비밀번호 없이, 외부 서비스가 일정한 권한만을 가지고 내 계정 정보를 사용할 수 있게 해주는 것이다. 사용자가 외부 앱에 로그인 요청을 하면, 실제 인증은 구글/카카오에게 맡기고, 사용자가 동의하면 access token을 발급해 외부 앱이 그 토큰으로 제한된 정보를 조회할 수 있게 해준다.

🔥 Trouble Shooting

  • 문제 상황 & 해결 방법 간단 정리
  1. @notblank
    Long타입에 @notblank를 써서 400에러가 계속 떴는데 @notblank는 string 타입에만 적용되고 다른 타입에는 @NotNull 또는 @notempty를 써야한다. @NotNull@notempty의 차이는 @notempty가 null 뿐만 아니라 비어있는 경우도 허용하지 않는다는 것이다.("" 허용 안함)
    따라서,,,
    Long에 쓴다면 → @NotNull
    List에 쓴다면 → @notempty
    String에 쓴다면 → @notblank

  2. 토큰 앞뒤에 불필요한 공백 포함 -> trim()을 통해 공백 제거

  3. spring bean validation을 써서 message를 넣어줬음에도 응답 message에는 해당 validation message가 안들어갔었음
    NotBlank, @notempty 등 Spring Bean Validation에서 message를 지정해도, GlobalExceptionHandler에서 메시지를 꺼내지 않으면 응답에 포함되지 않는다는 점을 몰랐어서 메시지를 응답에 포함하도록 수정

🤔 고민해본 점

  • Tag 최대 두개까지 선택 가능 -> Tag 테이블, PostTag 테이블 추가
    기존에 한개만 선택 가능했던 태그였어서 post 엔티티가 tag도 함께 가지도록 했었는데 최대 두개 선택으로 바뀌면서 엔티티가 수정 필요,..
    이때 고민했던 부분은 Post 엔티티 안에 tag1, tag2처럼 두 개의 필드를 따로 둘지, 아니면 Tag 테이블을 따로 만들고 PostTag라는 중간 테이블을 두어 다대다(N:M) 관계로 연결할지를 결정하는 부분이었습니다!
    저는 후자를 선택했는데 이유는
  1. 태그로 게시글을 검색하거나 필터링하는 쿼리를 작성할 때 성능이 더 좋다고 생각했습니다.
    예를 들어, "서버" 태그가 포함된 게시글을 조회하는 경우에도 JOIN만으로 효율적인 검색이 가능
    2.확장성과 유연성 측면에서 유리.
    현재는 최대 두 개지만, 이후 요구사항이 변경되어 무제한으로 늘어나더라도 테이블 구조를 바꿀 필요가 없다고 생각했습니다.
  2. 정규화에 따라 데이터 중복을 방지할 수 있고, 각 태그를 독립적으로 관리하기 편리
    동일한 태그가 여러 게시글에서 중복되더라도 하나의 Tag 레코드를 참조함으로써 데이터 일관성을 유지할 수 있다고 생각했습니다.
  • 좋아요 누르기/취소하기 -> save/delete 방식 선택
    좋아요 기능을 구현할 때 여러 가지 방법이 존재했는데, 저는 좋아요를 누르면 Like 객체가 생성되고, 취소하면 삭제되는 방식(save/delete) 으로 구현했습니다.
    처음에는 boolean 상태값으로 구현할까도 고민했지만, 이 경우 좋아요를 누른 뒤 취소한 레코드도 계속해서 DB에 남게 되어 데이터가 불필요하게 많이 쌓일 수 있다는 점이 걱정되었습니다. 실제로는 유저가 한 번만 좋아요를 눌렀다가 취소하는 경우도 많기 때문에, 삭제하는 것이 더 깔끔하다고 판단했습니다.
    다만 이 방식은 좋아요 누르기와 취소 시마다 insert/delete 쿼리가 발생하기 때문에, boolean 필드를 update하는 방식보다 성능적으로 불리할 수 있다는 우려도 있었습니다. 특히 좋아요/취소가 반복되는 경우엔 write I/O가 더 많아질 수 있습니다.
    하지만 현재 서비스 규모에서는 이 방법이 낫다고 생각했고, 좋아요 수를 구할 때도 count 쿼리로 쉽게 처리할 수 있어 실용적이라고 느꼈습니다.
    서비스가 커지게 되면 어떤 방식을 많이 사용하고 다른 분들은 어떤 방식을 쓰셨는지 궁금합니다!

  • @AuthenticationPrincipal 사용
    userId를 HTTP 헤더에 직접 넣지 않고 JWT 필터에서 Authentication 객체를 만들고 그 안에 userId를 principal로 설정해서 이후 Spring MVC가 @AuthenticationPrincipal로 등록된 파라미터를 Authentication.getPrincipal()에서 꺼내서 주입하도록 했습니다.
    이렇게 할 경우,,
    HTTP 요청은 Stateless하기 때문에, 매 요청마다 JWT를 통해 인증 여부를 확인해야하는데 이 과정을 필터에서 수행하고, SecurityContext에 한 번 등록하면,그 뒤에는 Spring이 알아서 @AuthenticationPrincipal에 주입해주기 때문에 컨트롤러에서 인증에 신경을 쓸 필요가 없기 때문에 @AuthenticationPrincipal 를 사용했습니다!

📢 To Reviewers

다들 바쁘신데 시간 내 리뷰해주셔서 감사합니다🙇‍♀️

📷 Screenshot

스크린샷 2025-06-25 오후 6 07 49 스크린샷 2025-06-25 오후 6 08 12 스크린샷 2025-06-27 오후 3 19 04 스크린샷 2025-06-27 오후 3 20 08 스크린샷 2025-06-27 오후 3 20 33

@jeong724 jeong724 self-assigned this Jun 25, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants