[feat] 커뮤니티 태그/좋아요/인기글 기능 및 API 응답 개선, JWT sub 표준화#109
[feat] 커뮤니티 태그/좋아요/인기글 기능 및 API 응답 개선, JWT sub 표준화#109iamseojin merged 5 commits intowith-travel:developfrom
Conversation
There was a problem hiding this comment.
Pull Request Overview
This PR implements community tagging, like functionality, and JWT standardization to enhance the community platform. The changes focus on improving user experience with categorized content browsing and engagement features while adhering to JWT best practices.
- Standardizes JWT token parsing to use
sub(subject) for user email instead ofaud(audience) - Introduces community tags (RESTAURANT, CAFE, INFO, TIPS) for content categorization
- Adds like/unlike functionality with toggle API and popular posts feature
Reviewed Changes
Copilot reviewed 19 out of 19 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| SecurityUtils.java | Enhanced principal type handling to support both AuthenticatedMember and PrincipalDetails |
| JwtProvider.java | Refactored JWT generation and parsing to use standard sub claim for user identification |
| JwtFilter.java | Updated authentication flow to use new email extraction method |
| CommunityTag.java | New enum defining 4 community categories with Korean labels |
| Community.java | Added tag, like count, and reply count fields with database indexes |
| CommunityService.java | Implemented tag filtering, like toggle, and popular posts functionality |
| Various DTOs | Updated to include tag and engagement metrics in API responses |
Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.
src/main/java/com/arom/with_travel/domain/community/Community.java
Outdated
Show resolved
Hide resolved
| // .parseSignedClaims(token); | ||
| // if (claims.getPayload() | ||
| // .getExpiration() | ||
| // .before(new Date())) { | ||
| // throw BaseException.from(EXPIRED_ACCESS_TOKEN); | ||
| // } | ||
| // return claims.getPayload() | ||
| // .getAudience() | ||
| // .iterator() | ||
| // .next(); | ||
| // } catch (JwtException | IllegalArgumentException e) { | ||
| // log.warn("[parseAudience] {} :{}", INVALID_TOKEN, token); | ||
| // throw BaseException.from(INVALID_TOKEN); | ||
| // } catch (BaseException e) { | ||
| // log.warn("[parseAudience] {} :{}", EXPIRED_ACCESS_TOKEN, token); | ||
| // throw BaseException.from(EXPIRED_ACCESS_TOKEN); | ||
| // } | ||
| // } |
There was a problem hiding this comment.
Remove commented-out code block. Dead code should be deleted rather than commented out to maintain code cleanliness and reduce confusion.
| // .parseSignedClaims(token); | |
| // if (claims.getPayload() | |
| // .getExpiration() | |
| // .before(new Date())) { | |
| // throw BaseException.from(EXPIRED_ACCESS_TOKEN); | |
| // } | |
| // return claims.getPayload() | |
| // .getAudience() | |
| // .iterator() | |
| // .next(); | |
| // } catch (JwtException | IllegalArgumentException e) { | |
| // log.warn("[parseAudience] {} :{}", INVALID_TOKEN, token); | |
| // throw BaseException.from(INVALID_TOKEN); | |
| // } catch (BaseException e) { | |
| // log.warn("[parseAudience] {} :{}", EXPIRED_ACCESS_TOKEN, token); | |
| // throw BaseException.from(EXPIRED_ACCESS_TOKEN); | |
| // } | |
| // } |
| public void validateAudience(String token, String expectedAud) { | ||
| try { | ||
| Jws<Claims> claims = Jwts.parser() | ||
| .verifyWith(SECRET_KEY) | ||
| .build() | ||
| .parseSignedClaims(token); | ||
| if (claims.getPayload() | ||
| .getExpiration() | ||
| .before(new Date())) { | ||
| Jws<Claims> claims = Jwts.parser().verifyWith(SECRET_KEY).build().parseSignedClaims(token); | ||
| Claims body = claims.getPayload(); | ||
| if (body.getExpiration().before(new Date())) { | ||
| throw BaseException.from(EXPIRED_ACCESS_TOKEN); | ||
| } | ||
| return claims.getPayload() | ||
| .getAudience() | ||
| .iterator() | ||
| .next(); | ||
| } catch (JwtException | IllegalArgumentException e) { | ||
| log.warn("[parseAudience] {} :{}", INVALID_TOKEN, token); | ||
| throw BaseException.from(INVALID_TOKEN); | ||
| var audiences = body.getAudience(); | ||
| if (audiences == null || !audiences.contains(expectedAud)) { | ||
| throw BaseException.from(INVALID_TOKEN); | ||
| } | ||
| } catch (BaseException e) { | ||
| log.warn("[parseAudience] {} :{}", EXPIRED_ACCESS_TOKEN, token); | ||
| // 만료 등 우리 쪽 예외만 캐치해서 동일 코드로 재던짐(스크린샷 흐름 반영) | ||
| throw BaseException.from(EXPIRED_ACCESS_TOKEN); | ||
| } |
There was a problem hiding this comment.
The validateAudience method is missing a catch block for JwtException which could cause unhandled exceptions. Add a catch block for JwtException to throw INVALID_TOKEN, similar to the original parseAudience method.
…java Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
이슈
구현 기능
변경 요약
JWT 파싱 기준 변경: 토큰에서 이메일을 aud가 아닌 sub로 파싱하도록 변경
커뮤니티 태그 도입(4종): RESTAURANT(맛집추천), CAFE(카페탐방), INFO(정보공유), TIPS(꿀팁)
좋아요 기능 추가
수정/삭제 응답 정리
SecurityUtils 보강
주요 변경 파일/포인트
변경 이유