Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
📝 Work Description
⭐️ 필수 과제
🥇 선택 과제
💡 키워드 과제
🔥 이런 고민들이 있습니다!
댓글 기능💡
좋아요 기능💡
게시글과 댓글은 좋아요 처리 방식이 유사하지만, Redis key 형식과 저장 방식이 다릅니다. 하지만, LikeService에서 분기 처리를 해주는 것보단 PostLikeCacheStrategy, CommentLikeCacheStrategy로 분리하는 것이 더 확장성을 보장해준다고 생각해서 Strategy 패턴을 구현해보게 됐습니다.
그리고 캐시 처리 로직을 global 패키지에 둬야 할지, like 도메인 패키지에 둬야 할지 고민을 했었습니다. 좋아요 캐시는 도메인 로직에 깊이 결합되어 있기 때문에 범용성이 없다고 판단이 돼서, 좋아요 관련 캐시 처리 로직은 like 도메인 패키지에 분리하게 됐습니다.
페이지네이션💡
검색 기능 - QueryDSL(예정) ❌
태그 기능 확장💡
그리고, 태그 필터링 성능 향상을 위해 post_tags.tag 컬럼에 인덱스를 추가했습니다. 태그는 enum 기반으로 고정된 값 집합이라 cardinality가 낮긴 하지만, 검색 조건으로 자주 사용되며 특정 태그에 수천 개 게시글이 몰릴 수 있다는 생각으로 인덱스를 설정하게 됐습니다. 인덱스는 조회 성능을 높여주는 대신, 쓰기 성능을 약간 저하시킬 수 있지만, 쓰기 비용 증가보다 조회 성능 향상이 중요한 구조로 판단했습니다. 따라서, 인덱스를 통해 `Full Scan` 없이 tag 조건을 만족하는 row의 인덱스만 먼저 훑고, 빠르게 결과를 조회할 수 있도록 설계했습니다.
병렬 처리 & 비동기화 ❌
JWT 토큰 기반 로그인 기능💡
1️⃣ Refresh Token을 DB에서 따로 관리해주게 된 이유 - stateless한 jwt를 서버에서 무효화할 방법은 없지만, db에서 따로 관리해주게 되면 db에서 삭제함으로써 무효화가 가능하게끔 구현했습니다. - 클라이언트가 토큰을 삭제한다고 해도 토큰 자체는 여전히 유효하므로 탈취당한 토큰이라면 만료까지 계속 사용 가능하게 됩니다. 그래서 로그아웃을 했다면 refresh token으로 더이상 재발급이 불가능하도록, 즉 진짜 로그아웃이 가능하도록 하고 싶었습니다. - 다중 기기에서 로그인 했을 경우 하나의 기기에서만 로그인이 될 수 있도록 하는 기능도 생각했는데, 다른 기기에서 로그인을 시도하면 기존 토큰(리프레쉬)을 삭제하고 새로운 토큰을 저장하게 됩니다. 그럼 기존에 로그인 돼있던 핸드폰에서 refresh token으로 재발급이 불가능하게 되고, access Token이 만료됐을 때 로그인이 차단되도록 했습니다. (근데, 카카오톡 같은 걸 생각해보면 다른 기기에서 로그인 시에 즉시 차단되도록 했는데, 아마 redis를 이용해서 매 api 요청마다 체크하도록 하는 것이 아닐까? 라는 생각이 들었어요.)
2️⃣ `@ExtractRefreshToken`을 만든 이유 - Access Token도 헤더에서 추출하기 때문에, 토큰 재발급 시에 Refresh Token도 헤더에서 추출하는게 더 적절하겠다는 생각에 따로 추출할 수 있는 어노테이션을 만들었는데, 사실 코드 통일성만 높였다 느낌이지, 코드가 더 늘기만 한 느낌이라 잘 모르겠습니다 ㅎㅎ..
3️⃣ 패키지 구조를 어떻게 해야할지 고민이 정말 많습니다.. - 일단은 JWT를 생성하거나 파싱하는 과정과 관련된 코드들은 `global/security`에 두고, 토큰 관리와 같은 책임을 담당하는 코드들은 auth 패키지에 뒀는데, 깔끔하다는 생각은 안드네요.. OAuth도 마찬가지로 구조를 어떻게 설계해야할지 감이 잘 안잡혀서.. 혹시 더 좋은 방법을 알고 계시다면 공유좀 부탁드립니다!
4️⃣ 예외 처리 전략 - 인증이 필요한, 즉 SecurityFilterChain을 거치는 과정에서의 예외는 필터(JwtExceptionFiter)에서 처리하도록 했고, 이외에 필터를 거치지 않거나 이미 거친 이후에 비즈니스 로직 흐름 속에서 JWT 검증 및 파싱 과정에서 발생하는 예외는 GlobalExceptionHandler에서 잡게끔 설계했습니다.
5️⃣ 소셜 로그인 구현 과정에서의 확장성 고려 - 각 클래스가 맡고 있는 책임을 간단하게 정리해놨는데, 이후 구글, 네이버, 애플과 같은 로그인도 구현하게 될 경우 `OAuthService`에서 모든 것을 처리하게 됐을 때 책임 분리도 불분명해지고 재사용성 및 확장성도 떨어지게 될 것이라는 생각이 들어서 이렇게 설계해보게 됐습니다.
AuthService: 토큰 발급/재발급/로그아웃 (인증 흐름 제어) OAuthServiceProvider: 소셜 플랫폼 라우팅 역할 KakaoOAuthServiceImpl: 카카오 API 통신 SocialUserProvider: 사용자 생성/연동 로직 (도메인 로직)
📷 Screenshot