Skip to content

Refactor like feature and add member nickname field#15

Open
kminseok-dev wants to merge 4 commits intomainfrom
develop
Open

Refactor like feature and add member nickname field#15
kminseok-dev wants to merge 4 commits intomainfrom
develop

Conversation

@kminseok-dev
Copy link
Owner

@kminseok-dev kminseok-dev commented Feb 21, 2026

Summary by CodeRabbit

릴리스 노트

  • 새 기능

    • 회원 프로필에 닉네임 필드 추가
    • 특정 사용자가 좋아한 게시글을 페이지 단위로 조회하는 기능 추가
    • 좋아요 관련 API 경로 구조 개선(더 직관적인 엔드포인트)
  • 테스트

    • API 연동·흐름을 위한 HTTP 요청 스크립트 추가
  • 문서

    • README 프로젝트 설명 및 방향 업데이트

@coderabbitai
Copy link

coderabbitai bot commented Feb 21, 2026

Walkthrough

레포의 README 내용이 수정되었고, 멤버 엔티티/DTO에 닉네임 필드가 추가되었으며, 좋아요 기능이 Redis 캐싱을 활용하도록 서비스 레이어가 리팩토링되고 컨트롤러의 API 경로가 재구성되었습니다. 또한 HTTP 테스트 시나리오 파일이 신규 추가되었습니다.

Changes

Cohort / File(s) Summary
프로젝트 문서
README.md
프로젝트 헤더 및 간단한 설명 문구 재작성(텍스트 변경만).
멤버 엔티티 및 DTO
src/main/java/com/kms/springboard/member/entity/MemberEntity.java, src/main/java/com/kms/springboard/member/dto/MemberDto.java
nickname 필드 추가(검증 포함) 및 멤버 컬럼명 스네이크 케이스로 변경(memberIdmember_id, userIduser_id).
멤버 서비스
src/main/java/com/kms/springboard/member/service/MemberServiceImpl.java
회원 저장 시 DTO의 nickname을 엔티티에 매핑하도록 추가.
라이크 컨트롤러
src/main/java/com/kms/springboard/like/controller/LikeController.java
클래스 레벨 RequestMapping 변경(/api/boards/{boardId}/api/boards), 엔드포인트 경로 재배치(예: POST /{boardId}/like, GET /{boardId}/like/count), 인증 파라미터 일부 제거 및 GET /like/{userId}(사용자가 좋아요한 게시글 조회) 신규 추가.
라이크 서비스
src/main/java/com/kms/springboard/like/service/LikeService.java, src/main/java/com/kms/springboard/like/service/LikeServiceImpl.java
isLikeByUserId 공개 메서드 제거, 좋아요 토글 로직 리팩토링 및 Redis 기반 캐시/카운트 관리로 변경(추가된 private 헬퍼: addLike, removeLike, incrementLikeCount, decrementLikeCount, cacheUserLiked 등).
라이크 저장소
src/main/java/com/kms/springboard/like/repository/LikeRepository.java
java.util.Optional import 추가(메서드 서명/로직 변경 없음).
HTTP 테스트 명세
src/main/resources/http/RequestTest.http
회원가입→로그인→토큰 저장 후 회원조회, 게시글 CRUD, 좋아요 호출을 포함하는 신규 HTTP 테스트 시나리오 파일 추가.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant LikeController
    participant LikeService
    participant LikeRepository
    participant Redis
    Client->>LikeController: POST /api/boards/{boardId}/like (userId)
    LikeController->>LikeService: toggleLike(boardId, userId)
    alt not liked
        LikeService->>LikeRepository: save LikeEntity(boardId, userId)
        LikeRepository-->>LikeService: persisted
        LikeService->>Redis: INCR like:count:{boardId}
        Redis-->>LikeService: newCount
        LikeService->>Redis: SETEX like:user:{boardId}:{userId} true (TTL)
        Redis-->>LikeService: ok
        LikeService-->>LikeController: true (liked)
    else already liked
        LikeService->>LikeRepository: delete By(boardId, userId)
        LikeRepository-->>LikeService: deleted
        LikeService->>Redis: DECR like:count:{boardId}
        Redis-->>LikeService: newCount
        LikeService->>Redis: DEL like:user:{boardId}:{userId}
        Redis-->>LikeService: ok
        LikeService-->>LikeController: false (unliked)
    end
    LikeController-->>Client: 200 {message, likeCount}
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 닉네임 퐁당, Redis로 좋아요 폴짝,
라우트는 정돈되고 테스트는 줄지어,
코드 밭을 뛰어다니며 당근 한 입,
변경은 가볍게, 마음은 높이! 🥕✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed Pull request 제목이 변경 사항의 주요 내용을 정확하게 반영하고 있으며, 좋아요 기능 리팩토링과 회원 닉네임 필드 추가라는 두 가지 핵심 변경 사항을 명확하게 요약하고 있습니다.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch develop

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (5)
src/main/java/com/kms/springboard/member/entity/MemberEntity.java (1)

17-23: ⚠️ Potential issue | 🟠 Major

@IndexcolumnList가 변경된 컬럼명과 불일치

@Column(name = "user_id")로 컬럼명이 변경되었지만 @IndexcolumnList = "userId"는 이전 camelCase 이름을 그대로 참조합니다. JPA/Hibernate가 스키마 생성 또는 검증 시 존재하지 않는 컬럼명으로 인덱스를 생성하려 하여 오류가 발생할 수 있습니다.

🐛 제안 수정
 `@Table`(
         name="members",
         indexes = {
-                `@Index`(name = "uk_members_userId", columnList = "userId",unique = true),
+                `@Index`(name = "uk_members_user_id", columnList = "user_id", unique = true),
                 `@Index`(name = "uk_members_email", columnList = "email", unique = true)
         }
 )
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/kms/springboard/member/entity/MemberEntity.java` around
lines 17 - 23, The `@Index` columnList entries in MemberEntity reference the old
camelCase property name ("userId") but the actual DB column is renamed via
`@Column`(name = "user_id"); update the `@Index` annotations in the `@Table` on
MemberEntity to use the physical column names (e.g., change "userId" to
"user_id") and verify the "email" index matches the `@Column`(name=...) for the
email field so JPA/Hibernate creates/validates indexes against the correct
column names.
src/main/java/com/kms/springboard/member/service/MemberServiceImpl.java (1)

36-43: ⚠️ Potential issue | 🟡 Minor

닉네임 중복 검사 누락

userIdemail은 명시적 중복 검사(existsBy*)를 수행하지만 nickname은 생략되어 있습니다. MemberEntityunique = true DB 제약으로 인해 중복 시 DataIntegrityViolationException이 발생하며, 사용자에게 비친화적인 500 에러가 반환됩니다.

MemberRepositoryexistsByNickname을 추가하고 서비스 레이어에서도 일관되게 검사하세요.

🛡️ 제안 수정
+        if(memberRepository.existsByNickname(memberDto.getNickname())) {
+            throw new IllegalStateException("이미 존재하는 닉네임입니다");
+        }
         String encoded = passwordEncoder.encode(memberDto.getPassword());
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/kms/springboard/member/service/MemberServiceImpl.java`
around lines 36 - 43, The service is missing a pre-save uniqueness check for
nickname causing DB constraint violations; add an existsByNickname(String
nickname) method to MemberRepository and call it from MemberServiceImpl (before
building/saving the MemberEntity) to throw a controlled exception (e.g.,
DuplicateMemberException or IllegalArgumentException) when
memberDto.getNickname() already exists, mirroring the existing
existsByUserId/existsByEmail checks and preventing memberRepository.save(member)
from triggering DataIntegrityViolationException for MemberEntity.nickname.
src/main/java/com/kms/springboard/like/service/LikeServiceImpl.java (3)

78-104: ⚠️ Potential issue | 🟠 Major

트랜잭션 롤백 시 Redis 상태 불일치

addLike/removeLike@Transactional 컨텍스트 안에서 실행되지만, Redis 연산(incrementLikeCount, cacheUserLiked 등)은 JPA 트랜잭션의 롤백 대상이 아닙니다. DB 저장이 롤백되더라도 Redis 카운트와 캐시된 상태는 그대로 남아 최대 24시간 동안 불일치 상태가 유지됩니다.

DB 커밋 성공 이후에만 Redis 연산이 실행되도록 TransactionSynchronizationManager를 활용한 afterCommit 콜백 방식을 고려하세요.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/kms/springboard/like/service/LikeServiceImpl.java` around
lines 78 - 104, The DB/Redis inconsistency occurs because addLike and removeLike
call Redis helpers (incrementLikeCount, decrementLikeCount, cacheUserLiked)
inside the transactional method so Redis updates aren't rolled back with the DB;
change both addLike and removeLike to register a
TransactionSynchronizationManager.afterCommit callback that performs the Redis
operations (incrementLikeCount/decrementLikeCount and cacheUserLiked) only after
the transaction successfully commits, i.e., replace the direct calls in
addLike/removeLike with a
TransactionSynchronizationManager.registerSynchronization(...) that invokes
those specific methods (incrementLikeCount, decrementLikeCount, cacheUserLiked)
on afterCommit and handle any exceptions there to avoid affecting DB commit
flow.

106-121: ⚠️ Potential issue | 🟠 Major

캐시 키 미초기화 시 increment/decrement가 잘못된 카운트를 기록

Redis에 해당 키가 존재하지 않을 때 increment()는 0에서 시작해 1로 설정합니다. getLikeCount()를 한 번도 호출하지 않아 캐시가 비어 있는 상태에서 addLike가 실행되면, DB의 실제 좋아요 수(예: 50)와 무관하게 Redis에 1이 기록됩니다. 이후 getLikeCount()는 캐시 히트로 인해 잘못된 값을 반환합니다.

increment/decrement 전에 키 존재 여부를 확인하거나, 키가 없을 경우 DB에서 읽어 초기화하는 로직을 추가하세요.

🐛 제안 수정
 private void incrementLikeCount(Long boardId){
     String cacheKey = LIKE_COUNT_PREFIX + boardId;
-    redisTemplate.opsForValue().increment(cacheKey);
+    Boolean keyExists = redisTemplate.hasKey(cacheKey);
+    if (Boolean.TRUE.equals(keyExists)) {
+        redisTemplate.opsForValue().increment(cacheKey);
+    } else {
+        long count = likeRepository.countByBoardId(boardId);
+        redisTemplate.opsForValue().set(cacheKey, String.valueOf(count), CACHE_TTL);
+        return;
+    }
     redisTemplate.expire(cacheKey,CACHE_TTL);
 }

 private void decrementLikeCount(Long boardId){
     String cacheKey = LIKE_COUNT_PREFIX + boardId;
-    Long count = redisTemplate.opsForValue().decrement(cacheKey);
-    if(count != null && count < 0){
-        redisTemplate.opsForValue().set(cacheKey, "0", CACHE_TTL);
+    Boolean keyExists = redisTemplate.hasKey(cacheKey);
+    if (Boolean.TRUE.equals(keyExists)) {
+        Long count = redisTemplate.opsForValue().decrement(cacheKey);
+        if(count != null && count < 0){
+            redisTemplate.opsForValue().set(cacheKey, "0", CACHE_TTL);
+        }
+        redisTemplate.expire(cacheKey, CACHE_TTL);
+    } else {
+        long count = likeRepository.countByBoardId(boardId);
+        redisTemplate.opsForValue().set(cacheKey, String.valueOf(count), CACHE_TTL);
     }
-    log.debug("like count decremented in Redis: boardId={}", boardId);
+    log.debug("like count decremented in Redis: boardId={}", boardId);
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/kms/springboard/like/service/LikeServiceImpl.java` around
lines 106 - 121, incrementLikeCount and decrementLikeCount can create incorrect
counts when the Redis key is missing; modify both methods to first check whether
the key (LIKE_COUNT_PREFIX + boardId) exists and if not, load the current like
count from the database (e.g., via the repository method used by getLikeCount or
a countLikesByBoardId) and set that value into Redis with CACHE_TTL, then
perform the increment()/decrement() and reset the TTL; also ensure decrement
handles negative results by clamping to 0 after decrement. Use the existing
symbols incrementLikeCount, decrementLikeCount, LIKE_COUNT_PREFIX, CACHE_TTL,
redisTemplate.opsForValue(), and the service/repository method that reads DB
counts (the same one getLikeCount uses) to initialize the cache before mutating.

38-48: ⚠️ Potential issue | 🟠 Major

USER_LIKED_PREFIX 캐시가 쓰기만 하고 읽지 않음

cacheUserLiked()는 사용자별 좋아요 상태를 Redis에 기록하지만, toggleLike()의 존재 확인은 여전히 likeRepository.existsByBoardIdAndUserId()(DB 쿼리)를 사용합니다. Redis 캐시가 전혀 활용되지 않아 캐싱 효과가 없고 불필요한 쓰기 연산만 발생합니다.

Redis 캐시를 실제로 읽도록 toggleLike를 수정하거나, 사용되지 않는 cacheUserLiked 호출을 제거하세요.

🧹 Nitpick comments (4)
src/main/java/com/kms/springboard/like/controller/LikeController.java (1)

27-34: 변수명 islikedisLiked (Java 네이밍 컨벤션)

✏️ 제안 수정
-        boolean isliked = likeService.toggleLike(boardId, userId);
-
-        String message;
-        if(isliked){
+        boolean isLiked = likeService.toggleLike(boardId, userId);
+
+        String message;
+        if(isLiked){
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/kms/springboard/like/controller/LikeController.java` around
lines 27 - 34, Rename the local boolean variable isliked to isLiked in
LikeController (the variable that captures the return value of
likeService.toggleLike(boardId, userId)) to follow Java camelCase convention,
and update all references in the method (including the if(isliked) conditional
and any further usages) to use isLiked instead.
src/main/java/com/kms/springboard/like/repository/LikeRepository.java (1)

5-5: 사용되지 않는 Optional 임포트

java.util.Optional이 임포트되었으나 인터페이스 내 어떤 메서드에서도 사용되지 않습니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/kms/springboard/like/repository/LikeRepository.java` at
line 5, The import java.util.Optional at the top of LikeRepository.java is
unused; remove that unused import from the interface (LikeRepository) to clean
up warnings and keep imports accurate—no code changes beyond deleting the
Optional import line are needed.
src/main/java/com/kms/springboard/like/service/LikeServiceImpl.java (1)

73-76: getUserLikeBoards@Transactional(readOnly = true) 누락

getLikeUsers(Line 66)에는 @Transactional(readOnly = true)가 적용되어 있지만 동일한 조회 성격의 getUserLikeBoards에는 누락되어 있습니다. 클래스 레벨의 @Transactional이 적용되어 쓰기 가능 트랜잭션으로 실행됩니다.

♻️ 제안 수정
+    `@Transactional`(readOnly = true)
     public Page<LikeDto> getUserLikeBoards(String userId, Pageable pageable) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/kms/springboard/like/service/LikeServiceImpl.java` around
lines 73 - 76, The getUserLikeBoards method in LikeServiceImpl is missing
`@Transactional`(readOnly = true) and is currently running under the class-level
writable transaction; annotate the getUserLikeBoards method with
`@Transactional`(readOnly = true) so its read-only repository call
(likeRepository.findByUserId) executes in a read-only transaction consistent
with getLikeUsers.
src/main/java/com/kms/springboard/member/entity/MemberEntity.java (1)

27-34: 컬럼명 변경(memberIdmember_id, userIduser_id)은 DB 마이그레이션 필요

기존 운영 DB가 있다면 컬럼명 변경을 위한 DDL 마이그레이션 스크립트(Flyway 또는 Liquibase)가 없으면 애플리케이션 구동 시 스키마 불일치 오류가 발생합니다.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/kms/springboard/member/entity/MemberEntity.java` around
lines 27 - 34, The Entity MemberEntity now maps fields id -> column "member_id"
and userId -> "user_id", so add a DB migration (Flyway or Liquibase) that
renames the existing columns to these new names (preserving data and
constraints) before deploying; create a migration script that uses ALTER TABLE
... RENAME COLUMN (or the DB-specific equivalent) for memberId -> member_id and
userId -> user_id, include any index/constraint/name adjustments and test it
against the existing production schema, and ensure the migration file is
versioned and picked up by the project's migration runner so MemberEntity.id and
MemberEntity.userId mappings match the DB at startup.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/java/com/kms/springboard/like/controller/LikeController.java`:
- Around line 45-66: The two controller methods getLikeUsers and getLikeBoards
currently share the same GET mapping pattern ("/like/{...}") causing ambiguous
handler errors; change the mappings to distinct paths (e.g.,
`@GetMapping`("/like/board/{boardId}") for getLikeUsers and
`@GetMapping`("/like/user/{userId}") for getLikeBoards), update any clients/tests
to call the new endpoints, and keep the existing method signatures and calls to
likeService.getLikeUsers(...) and likeService.getUserLikeBoards(...) unchanged.

In `@src/main/java/com/kms/springboard/member/dto/MemberDto.java`:
- Around line 25-26: The MemberDto.nickname field is missing a `@Size`(max=...)
constraint and the corresponding entity column has no length limit, allowing
excessively long nicknames; add a `@Size`(max=XX, message="...") annotation to the
nickname field in MemberDto (choose the same max as other fields or a sensible
limit like 30), and update the corresponding Member entity's nickname column
annotation (e.g., `@Column`(length=XX)) to enforce the same maximum at the
DB/model level so DTO validation and the entity schema match.

In `@src/main/resources/http/RequestTest.http`:
- Around line 7-13: The test request JSON contains real PII in the fields
username, email, userId, and nickname and a weak password; replace these with
clearly synthetic values (e.g., username: "Test User", email:
"user@example.test", userId: "testuser123", nickname: "tester") and use a
non-trivial test password (e.g., "TestPass!234") so the data is obviously fake
and no real personal information is committed; update the JSON object in
RequestTest.http replacing the values for username, email, userId, password, and
nickname accordingly.

---

Outside diff comments:
In `@src/main/java/com/kms/springboard/like/service/LikeServiceImpl.java`:
- Around line 78-104: The DB/Redis inconsistency occurs because addLike and
removeLike call Redis helpers (incrementLikeCount, decrementLikeCount,
cacheUserLiked) inside the transactional method so Redis updates aren't rolled
back with the DB; change both addLike and removeLike to register a
TransactionSynchronizationManager.afterCommit callback that performs the Redis
operations (incrementLikeCount/decrementLikeCount and cacheUserLiked) only after
the transaction successfully commits, i.e., replace the direct calls in
addLike/removeLike with a
TransactionSynchronizationManager.registerSynchronization(...) that invokes
those specific methods (incrementLikeCount, decrementLikeCount, cacheUserLiked)
on afterCommit and handle any exceptions there to avoid affecting DB commit
flow.
- Around line 106-121: incrementLikeCount and decrementLikeCount can create
incorrect counts when the Redis key is missing; modify both methods to first
check whether the key (LIKE_COUNT_PREFIX + boardId) exists and if not, load the
current like count from the database (e.g., via the repository method used by
getLikeCount or a countLikesByBoardId) and set that value into Redis with
CACHE_TTL, then perform the increment()/decrement() and reset the TTL; also
ensure decrement handles negative results by clamping to 0 after decrement. Use
the existing symbols incrementLikeCount, decrementLikeCount, LIKE_COUNT_PREFIX,
CACHE_TTL, redisTemplate.opsForValue(), and the service/repository method that
reads DB counts (the same one getLikeCount uses) to initialize the cache before
mutating.

In `@src/main/java/com/kms/springboard/member/entity/MemberEntity.java`:
- Around line 17-23: The `@Index` columnList entries in MemberEntity reference the
old camelCase property name ("userId") but the actual DB column is renamed via
`@Column`(name = "user_id"); update the `@Index` annotations in the `@Table` on
MemberEntity to use the physical column names (e.g., change "userId" to
"user_id") and verify the "email" index matches the `@Column`(name=...) for the
email field so JPA/Hibernate creates/validates indexes against the correct
column names.

In `@src/main/java/com/kms/springboard/member/service/MemberServiceImpl.java`:
- Around line 36-43: The service is missing a pre-save uniqueness check for
nickname causing DB constraint violations; add an existsByNickname(String
nickname) method to MemberRepository and call it from MemberServiceImpl (before
building/saving the MemberEntity) to throw a controlled exception (e.g.,
DuplicateMemberException or IllegalArgumentException) when
memberDto.getNickname() already exists, mirroring the existing
existsByUserId/existsByEmail checks and preventing memberRepository.save(member)
from triggering DataIntegrityViolationException for MemberEntity.nickname.

---

Nitpick comments:
In `@src/main/java/com/kms/springboard/like/controller/LikeController.java`:
- Around line 27-34: Rename the local boolean variable isliked to isLiked in
LikeController (the variable that captures the return value of
likeService.toggleLike(boardId, userId)) to follow Java camelCase convention,
and update all references in the method (including the if(isliked) conditional
and any further usages) to use isLiked instead.

In `@src/main/java/com/kms/springboard/like/repository/LikeRepository.java`:
- Line 5: The import java.util.Optional at the top of LikeRepository.java is
unused; remove that unused import from the interface (LikeRepository) to clean
up warnings and keep imports accurate—no code changes beyond deleting the
Optional import line are needed.

In `@src/main/java/com/kms/springboard/like/service/LikeServiceImpl.java`:
- Around line 73-76: The getUserLikeBoards method in LikeServiceImpl is missing
`@Transactional`(readOnly = true) and is currently running under the class-level
writable transaction; annotate the getUserLikeBoards method with
`@Transactional`(readOnly = true) so its read-only repository call
(likeRepository.findByUserId) executes in a read-only transaction consistent
with getLikeUsers.

In `@src/main/java/com/kms/springboard/member/entity/MemberEntity.java`:
- Around line 27-34: The Entity MemberEntity now maps fields id -> column
"member_id" and userId -> "user_id", so add a DB migration (Flyway or Liquibase)
that renames the existing columns to these new names (preserving data and
constraints) before deploying; create a migration script that uses ALTER TABLE
... RENAME COLUMN (or the DB-specific equivalent) for memberId -> member_id and
userId -> user_id, include any index/constraint/name adjustments and test it
against the existing production schema, and ensure the migration file is
versioned and picked up by the project's migration runner so MemberEntity.id and
MemberEntity.userId mappings match the DB at startup.

Comment on lines +45 to +66
@GetMapping("/like/{boardId}")
public ResponseEntity<ApiResponse<Page<LikeDto>>> getLikeUsers(
@PathVariable Long boardId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20")int size,
Authentication auth){


@RequestParam(defaultValue = "20")int size){
Pageable pageable = PageRequest.of(page, size);
Page<LikeDto> users = likeService.getLikeUsers(boardId, pageable);

return ResponseEntity.ok(ApiResponse.success("좋아요 누른 사람 목록 조회", users));

}

@GetMapping("/like/{userId}")
public ResponseEntity<ApiResponse<Page<LikeDto>>> getLikeBoards(
@PathVariable String userId,
@RequestParam(defaultValue = "0") int page,
@RequestParam(defaultValue = "20") int size){
Pageable pageable = PageRequest.of(page, size);
Page<LikeDto> userLikeBoards = likeService.getUserLikeBoards(userId, pageable);

return ResponseEntity.ok(ApiResponse.success("사용자가 좋아요 누른 게시글", userLikeBoards));
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

/like/{boardId}/like/{userId}가 동일한 URL 패턴 → 애플리케이션 구동 불가

두 엔드포인트의 경로가 모두 GET /api/boards/like/{variable}로 동일합니다. Spring MVC는 경로 변수의 타입(Long vs String)으로 라우팅을 구분하지 않으며, 이 경우 IllegalStateException: Ambiguous handler methods 예외가 발생합니다. 애플리케이션이 정상적으로 시작되지 않습니다.

두 엔드포인트를 구분 가능한 경로로 분리하세요.

🐛 제안 수정
-    `@GetMapping`("/like/{boardId}")
+    `@GetMapping`("/{boardId}/like/users")
     public ResponseEntity<ApiResponse<Page<LikeDto>>> getLikeUsers(
             `@PathVariable` Long boardId,
             `@RequestParam`(defaultValue = "0") int page,
-            `@RequestParam`(defaultValue = "20")int size){
+            `@RequestParam`(defaultValue = "20") int size){
         Pageable pageable = PageRequest.of(page, size);
         Page<LikeDto> users = likeService.getLikeUsers(boardId, pageable);
         return ResponseEntity.ok(ApiResponse.success("좋아요 누른 사람 목록 조회", users));
     }

-    `@GetMapping`("/like/{userId}")
+    `@GetMapping`("/users/{userId}/like")
     public ResponseEntity<ApiResponse<Page<LikeDto>>> getLikeBoards(
             `@PathVariable` String userId,
             `@RequestParam`(defaultValue = "0") int page,
             `@RequestParam`(defaultValue = "20") int size){
         Pageable pageable = PageRequest.of(page, size);
         Page<LikeDto> userLikeBoards = likeService.getUserLikeBoards(userId, pageable);
         return ResponseEntity.ok(ApiResponse.success("사용자가 좋아요 누른 게시글", userLikeBoards));
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/kms/springboard/like/controller/LikeController.java` around
lines 45 - 66, The two controller methods getLikeUsers and getLikeBoards
currently share the same GET mapping pattern ("/like/{...}") causing ambiguous
handler errors; change the mappings to distinct paths (e.g.,
`@GetMapping`("/like/board/{boardId}") for getLikeUsers and
`@GetMapping`("/like/user/{userId}") for getLikeBoards), update any clients/tests
to call the new endpoints, and keep the existing method signatures and calls to
likeService.getLikeUsers(...) and likeService.getUserLikeBoards(...) unchanged.

Comment on lines +25 to +26
@NotBlank(message = "닉네임을 입력해주세요")
private String nickname;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

nickname 필드에 @Size 제약 누락

userId, email, password에는 모두 @Size 최댓값이 지정되어 있지만 nickname에는 없습니다. 대응하는 엔티티 컬럼에도 length 지정이 없어, 비정상적으로 긴 문자열이 입력될 수 있습니다.

✏️ 제안 수정
-    `@NotBlank`(message = "닉네임을 입력해주세요")
+    `@NotBlank`(message = "닉네임을 입력해주세요")
+    `@Size`(min = 2, max = 30, message = "닉네임은 2~30자여야 합니다")
     private String nickname;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/kms/springboard/member/dto/MemberDto.java` around lines 25
- 26, The MemberDto.nickname field is missing a `@Size`(max=...) constraint and
the corresponding entity column has no length limit, allowing excessively long
nicknames; add a `@Size`(max=XX, message="...") annotation to the nickname field
in MemberDto (choose the same max as other fields or a sensible limit like 30),
and update the corresponding Member entity's nickname column annotation (e.g.,
`@Column`(length=XX)) to enforce the same maximum at the DB/model level so DTO
validation and the entity schema match.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
README.md (1)

1-5: README에 기술 스택 및 실행 방법 추가를 권장합니다.

현재 내용은 프로젝트를 간략히 소개하는 수준에서 적절합니다. 다만, 이번 PR에서 Redis 캐싱, 닉네임 필드, HTTP 테스트 등 의미 있는 기술적 변경이 이루어졌으므로, 아래와 같이 기술 스택과 실행 방법을 함께 기술하면 프로젝트의 가독성과 재현 가능성이 높아집니다.

📝 README 보강 예시
 # 프로젝트
 
 - 개인 프로젝트
 - spring으로 게시판 만들기
 - 새로운 기술 적용해 계속 발전시키는 중
+
+## 기술 스택
+
+- Java / Spring Boot
+- Spring Data JPA
+- Redis (좋아요 캐싱)
+- MySQL
+
+## 실행 방법
+
+```bash
+# Redis 및 MySQL이 실행 중인 상태에서
+./gradlew bootRun
+```
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@README.md` around lines 1 - 5, Update README.md to include a concise "Tech
Stack" section listing core technologies used (e.g., Spring Boot, MySQL, Redis,
Gradle) and an "Running Locally" section with exact prerequisites and commands;
mention that Redis and MySQL must be running and show the command to start the
app (e.g., ./gradlew bootRun) and any environment variables or ports to set, and
add a brief note about running HTTP tests and the new nickname field so
reviewers can reproduce the Redis caching and nickname behavior locally.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@README.md`:
- Around line 1-5: Update README.md to include a concise "Tech Stack" section
listing core technologies used (e.g., Spring Boot, MySQL, Redis, Gradle) and an
"Running Locally" section with exact prerequisites and commands; mention that
Redis and MySQL must be running and show the command to start the app (e.g.,
./gradlew bootRun) and any environment variables or ports to set, and add a
brief note about running HTTP tests and the new nickname field so reviewers can
reproduce the Redis caching and nickname behavior locally.

Repository owner deleted a comment from coderabbitai bot Feb 21, 2026
@kminseok-dev kminseok-dev changed the title Develop Refactor like feature and add member nickname field Feb 21, 2026
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.

1 participant