Skip to content

Conversation

@sungchaewon
Copy link
Member

@sungchaewon sungchaewon commented May 28, 2025

이슈 번호

41
Closes #41

작업 내용

  • api 경로 통일(맨 앞에 /api 없게)
  • MEMBER NOT FOUND → 기존 USER ID NOT FOUND, USER EMAIL NOT FOUND와 의미 중복으로 정리
  • 회원 탈퇴 구현

Summary by CodeRabbit

  • 신규 기능

    • 회원가입, 로그인, 메인 페이지, 게시글, 팔로우, 북마크 폴더 및 북마크 관리 등 핵심 API 엔드포인트가 추가되었습니다.
    • JWT 기반 인증 및 Spring Security 설정이 도입되어 인증 및 권한 관리가 강화되었습니다.
    • 게시글 좋아요, 북마크, 팔로우/언팔로우 등 다양한 소셜 기능이 제공됩니다.
    • 메인 페이지에서 최신/인기 게시글 조회 및 페이징 기능이 지원됩니다.
    • 이메일/닉네임 중복 확인, 회원 정보 조회 등 회원 관련 기능이 추가되었습니다.
  • 버그 수정

    • 해당 릴리스에 포함된 버그 수정 사항은 없습니다.
  • 환경/설정

    • 데이터베이스(H2), JWT, JPA 등 주요 환경 및 보안 설정이 추가되었습니다.
    • 테스트 데이터 자동 삽입 기능이 도입되어 초기 개발 및 테스트가 용이해졌습니다.
  • 예외 처리 및 응답 포맷

    • 표준화된 응답 구조 및 글로벌 예외 처리 로직이 적용되어 일관된 에러 메시지를 제공합니다.

@sungchaewon sungchaewon linked an issue May 28, 2025 that may be closed by this pull request
3 tasks
@coderabbitai
Copy link
Contributor

coderabbitai bot commented May 28, 2025

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Walkthrough

이 변경 사항은 FixLog 서버의 주요 도메인, DTO, 서비스, 컨트롤러, 리포지토리, 예외 처리, 보안(JWT 인증 및 Spring Security), 그리고 테스트 데이터 초기화 기능을 대규모로 도입합니다. 회원가입, 로그인, 팔로우, 북마크, 포스트 등 핵심 기능의 전체 백엔드 구조가 구현되었습니다.

Changes

파일/경로 요약 변경 요약
.coderabbit.yaml, build.gradle, application.properties 코드리뷰 환경설정, JWT 및 관련 의존성 추가, DB/JPA/JWT 환경설정
src/main/java/com/example/FixLog/config/... JWT 인증 필터 및 Spring Security 설정 클래스 추가
src/main/java/com/example/FixLog/controller/... Auth, Member, Follow, BookmarkFolder, Post, MainPage 관련 REST 컨트롤러 일괄 추가
src/main/java/com/example/FixLog/domain/... Member, Follow, Bookmark, BookmarkFolder, Post, PostLike, PostTag, Fork, Tag, SocialType, TagCategory 등 주요 도메인 엔티티 및 enum 추가
src/main/java/com/example/FixLog/dto/... 회원, 팔로우, 북마크, 포스트, 메인페이지 등 전용 DTO 및 응답/요청 record/class 일괄 추가
src/main/java/com/example/FixLog/exception/... 커스텀 예외, 에러코드 enum, 글로벌 예외 핸들러 추가
src/main/java/com/example/FixLog/mock/... 테스트 데이터 초기화용 CommandLineRunner 컴포넌트 3종 추가
src/main/java/com/example/FixLog/repository/... Member, Follow, Bookmark, Post, Tag 등 JPA 리포지토리 일괄 추가
src/main/java/com/example/FixLog/service/... Auth, Member, Follow, Bookmark, BookmarkFolder, Post, MainPage 서비스 클래스 일괄 추가
src/main/java/com/example/FixLog/util/JwtUtil.java JWT 생성, 파싱, 검증 유틸리티 클래스 추가

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant AuthController
    participant AuthService
    participant MemberRepository
    participant PasswordEncoder
    participant JwtUtil

    Client->>AuthController: POST /auth/login (email, password)
    AuthController->>AuthService: login(requestDto)
    AuthService->>MemberRepository: findByEmail(email)
    MemberRepository-->>AuthService: Member or Exception
    AuthService->>PasswordEncoder: matches(raw, encoded)
    AuthService->>JwtUtil: createToken(userId, email)
    JwtUtil-->>AuthService: accessToken
    AuthService-->>AuthController: LoginResponseDto
    AuthController-->>Client: Response<LoginResponseDto>
Loading
sequenceDiagram
    participant Client
    participant SecurityConfig
    participant JwtAuthenticationFilter
    participant JwtUtil
    participant MemberRepository
    participant SecurityContextHolder

    Client->>SecurityConfig: 요청
    SecurityConfig->>JwtAuthenticationFilter: 요청 필터링
    JwtAuthenticationFilter->>JwtUtil: isTokenValid(token)
    JwtUtil-->>JwtAuthenticationFilter: true/false
    JwtAuthenticationFilter->>JwtUtil: getEmailFromToken(token)
    JwtAuthenticationFilter->>MemberRepository: findByEmail(email)
    MemberRepository-->>JwtAuthenticationFilter: Member
    JwtAuthenticationFilter->>SecurityContextHolder: setAuthentication
    JwtAuthenticationFilter-->>SecurityConfig: 필터 체인 계속
Loading

Assessment against linked issues

Objective Addressed Explanation
회원탈퇴 기능 구현 (#41) 회원탈퇴(탈퇴 처리) 관련 컨트롤러, 서비스, 엔티티 로직이 포함되어 있지 않음.

Possibly related PRs

  • [FEAT] 팔로우 구현 #10: 팔로우 기능 및 MemberRepository 추가 관련 변경이 포함되어 있으며, 본 PR의 FollowService, FollowController, MemberRepository와 코드 수준에서 연결됨.

Suggested labels

chaewon

Suggested reviewers

  • semsemin
  • youngseo22

Poem

🐰
새싹처럼 돋아난 코드들,
JWT로 보안 단단히,
팔로우, 북마크, 포스트까지
토끼는 깡총깡총 기뻐 뛰네!
FixLog의 봄이 왔어요,
개발자 마음에도 햇살 한 줌!
🥕✨


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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

@sungchaewon sungchaewon self-assigned this May 28, 2025
@sungchaewon sungchaewon added the del 쓸모없는 코드나 파일 삭제 label May 28, 2025
@sungchaewon sungchaewon marked this pull request as draft May 28, 2025 14:47
Copy link
Contributor

@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: 63

🧹 Nitpick comments (44)
.coderabbit.yaml (1)

15-15: 파일 끝 개행 추가 필요
YAMLlint 오류(no new line character at the end of file)를 해결하기 위해 파일 끝에 새 줄(개행)을 추가해주세요.

 .coderabbit.yaml
+<빈 줄 추가>
🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 15-15: no new line character at the end of file

(new-line-at-end-of-file)

src/main/java/com/example/FixLog/dto/follow/request/UnfollowRequestDto.java (1)

9-12: 중복 코드 제거를 위한 공통 DTO 클래스 고려해보세요.

현재 구현은 올바르지만, FollowRequestDto와 동일한 구조를 가지고 있습니다. 공통 기본 클래스나 제네릭을 활용하여 코드 중복을 줄일 수 있습니다.

공통 기본 클래스를 만들어 중복을 제거할 수 있습니다:

+@Getter
+@NoArgsConstructor
+public abstract class BaseTargetRequestDto {
+    @JsonProperty("target_member_id")
+    protected Long targetMemberId;
+}

-@Getter
-@NoArgsConstructor
-public class UnfollowRequestDto {
-    @JsonProperty("target_member_id")
-    private Long targetMemberId;
-}
+public class UnfollowRequestDto extends BaseTargetRequestDto {
+}
src/main/java/com/example/FixLog/dto/UserIdDto.java (1)

5-8: DTO의 불변성과 일관성을 개선하세요.

다른 DTO들과의 일관성을 위해 @NoArgsConstructor 어노테이션을 추가하고, 불변성을 위해 생성자나 빌더 패턴을 고려해보세요. 또한 userId 필드에 대한 유효성 검증을 추가하는 것을 권장합니다.

+import lombok.NoArgsConstructor;
+import javax.validation.constraints.NotNull;

 @Getter
+@NoArgsConstructor
 public class UserIdDto {
+    @NotNull
     private Long userId;
 }
src/main/java/com/example/FixLog/dto/bookmark/response/BookmarkFolderCreateResponse.java (1)

3-3: Java record 사용이 응답 DTO에 적합합니다.

Java record를 사용하여 불변성을 보장하는 것은 좋은 선택입니다. 다만 folder_id 필드명이 snake_case를 사용하고 있는데, Java 컨벤션상 camelCase(folderId)를 사용하는 것이 일반적입니다. API 응답 형식 요구사항에 따른 것이라면 @JsonProperty 어노테이션 사용을 고려해보세요.

만약 API 응답에서 snake_case가 필요하다면:

+import com.fasterxml.jackson.annotation.JsonProperty;
-public record BookmarkFolderCreateResponse(Long folder_id, String name) {}
+public record BookmarkFolderCreateResponse(
+    @JsonProperty("folder_id") Long folderId, 
+    String name
+) {}
src/main/java/com/example/FixLog/dto/member/SignupRequestDto.java (2)

9-12: DTO 필드에 유효성 검증 어노테이션 추가 제안
email, password, nickname 필드에 @NotBlank, @Email, @Size 등 Bean Validation 어노테이션을 추가하여 서버단 입력 유효성 검사를 강화하는 것을 권장합니다.


3-7: Lombok 어노테이션 일관성 점검
LoginRequestDto에는 @Getter만 사용하고, SignupRequestDto에는 @Getter/@Setter를 사용하고 있습니다. DTO의 불변성과 사용 방식을 고려해 어노테이션 정책을 통일하면 유지보수가 용이합니다.

src/main/java/com/example/FixLog/dto/member/LoginResponseDto.java (1)

11-14: 응답 필드 네이밍 컨벤션 확인
JSON 필드 네이밍(userId, profileImageUrl)이 camelCase로 노출됩니다. API 전체에서 snake_case(예: user_id)로 통일할지 팀 컨벤션을 검토하세요.

build.gradle (2)

28-28: 들여쓰기 일관성 확인
28번째 줄(implementation 'org.springframework.boot:spring-boot-starter-security')이 공백으로 들여쓰기되어 있습니다. 전체 파일에서 탭 혹은 공백 중 일관된 방식을 사용하세요.


30-32: JJWT 의존성 버전 관리 개선 제안
jjwt-api, jjwt-impl, jjwt-jackson 버전을 BOM으로 관리하면 하드코딩된 버전을 통일 관리할 수 있습니다. 예:

 dependencies {
-   implementation 'io.jsonwebtoken:jjwt-api:0.11.5'
-   runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.11.5'
-   runtimeOnly('io.jsonwebtoken:jjwt-jackson:0.11.5')
+   implementation platform('io.jsonwebtoken:jjwt-bom:0.11.5')
+   implementation 'io.jsonwebtoken:jjwt-api'
+   runtimeOnly 'io.jsonwebtoken:jjwt-impl'
+   runtimeOnly 'io.jsonwebtoken:jjwt-jackson'
 }
src/main/java/com/example/FixLog/dto/bookmark/request/BookmarkMoveRequest.java (1)

5-7: 필드 유효성 검증 어노테이션 추가 제안
folderId 필드에 @NotNull 또는 @Positive를 추가하여 필수 값임을 명시하고 음수 ID가 전달되는 것을 방지하세요.

src/main/java/com/example/FixLog/dto/bookmark/request/BookmarkFolderUpdateRequest.java (1)

5-7: 필드 유효성 검증 어노테이션 추가 제안
folderName@NotBlank, @Size(max = 50) 등을 추가해 빈 문자열이나 과도한 길이 전달을 방지하세요.

src/main/java/com/example/FixLog/dto/bookmark/request/BookmarkFolderCreateRequest.java (1)

5-7: 입력 값 검증을 위한 validation 어노테이션 추가를 고려하세요.

현재 구현은 깔끔하지만, 폴더명에 대한 유효성 검증이 없습니다.

다음과 같이 validation 어노테이션을 추가하는 것을 권장합니다:

+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;

public record BookmarkFolderCreateRequest(
-       @JsonProperty("name") String folderName
+       @JsonProperty("name") 
+       @NotBlank(message = "폴더명은 필수입니다")
+       @Size(max = 50, message = "폴더명은 50자를 초과할 수 없습니다")
+       String folderName
) {}
src/main/java/com/example/FixLog/mock/TagTestDataInitializer.java (1)

27-27: 로깅 시스템 사용을 고려하세요.

System.out.println 대신 로거를 사용하는 것이 더 적절합니다.

다음과 같이 개선할 수 있습니다:

+ import lombok.extern.slf4j.Slf4j;

+ @Slf4j
  @Component
  @RequiredArgsConstructor
  public class TagTestDataInitializer implements CommandLineRunner {
      // ...
-     System.out.println("임시 태그 4개 삽입 완료");
+     log.info("임시 태그 4개 삽입 완료");
src/main/java/com/example/FixLog/domain/tag/TagCategory.java (1)

5-19: 중복된 getter 메서드를 제거하여 일관성을 개선하세요.

@Getter 어노테이션이 클래스 레벨에 적용되어 있어 getDisplayName() 메서드가 자동으로 생성되므로, 수동으로 작성된 getter 메서드(17-19행)는 불필요합니다.

다음 diff를 적용하여 중복을 제거하세요:

-    public String getDisplayName() {
-        return displayName;
-    }
src/main/java/com/example/FixLog/domain/tag/Tag.java (2)

21-22: tagName 필드에 유효성 검증을 추가하는 것을 고려하세요.

데이터베이스 레벨 제약조건 외에 애플리케이션 레벨 검증도 추가하면 더 안전합니다.

다음과 같은 검증 어노테이션을 추가해보세요:

+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;

+@NotBlank(message = "태그명은 필수입니다")
+@Size(max = 20, message = "태그명은 20자를 초과할 수 없습니다")
 @Column(length = 20, nullable = false)
 private String tagName;

24-29: 정적 팩토리 메서드 패턴을 잘 활용했습니다.

of 메서드를 통한 객체 생성 방식이 깔끔하고 의도가 명확합니다. 다만 equals/hashCode 메서드 추가를 고려해보세요.

엔티티 동등성 비교를 위해 다음을 추가하는 것을 고려하세요:

+import lombok.EqualsAndHashCode;

+@EqualsAndHashCode(of = {"tagCategory", "tagName"})
 @Entity
 @Getter
 @NoArgsConstructor(access = AccessLevel.PROTECTED)
 public class Tag {
src/main/java/com/example/FixLog/dto/follow/request/FollowRequestDto.java (1)

7-12: 코드가 잘 구현되었습니다.

DTO 설계가 적절합니다:

  • JsonProperty 어노테이션으로 API 계약 일관성 확보
  • Lombok 어노테이션 적절한 사용
  • UnfollowRequestDto와 일관된 패턴

선택적 개선사항으로 입력 검증을 위한 Bean Validation 어노테이션을 고려할 수 있습니다:

+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Positive;

 public class FollowRequestDto {
+    @NotNull(message = "대상 회원 ID는 필수입니다")
+    @Positive(message = "회원 ID는 양수여야 합니다")
     @JsonProperty("target_member_id")
     private Long targetMemberId;  // 팔로우 대상 ID
 }
src/main/java/com/example/FixLog/mock/MemberTestDataInitializer.java (1)

37-37: 적절한 로깅 프레임워크를 사용하세요.

System.out.println 대신 SLF4J 로거를 사용하는 것이 좋습니다.

+import lombok.extern.slf4j.Slf4j;

+@Slf4j
 @Component
 @Profile({"dev", "test", "local"})
 @Order(1)
 @RequiredArgsConstructor
 public class MemberTestDataInitializer implements CommandLineRunner {

-            System.out.println("테스트용 멤버 2명 삽입 완료");
+            log.info("테스트용 멤버 2명 삽입 완료");
src/main/java/com/example/FixLog/util/JwtUtil.java (2)

21-21: 만료 시간을 설정 가능하도록 개선하세요.

만료 시간이 하드코딩되어 있습니다. 이를 application.properties에서 설정할 수 있도록 변경하는 것이 좋습니다.

+    @Value("${jwt.expiration}")
-    private final long expiration = 1000 * 60 * 60 * 24; // 24시간
+    private long expiration;

그리고 application.properties에 다음을 추가하세요:

jwt.expiration=86400000

46-53: 예외 처리를 더 구체적으로 개선하세요.

현재 모든 예외를 동일하게 처리하고 있습니다. JWT 관련 특정 예외들을 구별하여 처리하면 디버깅과 로깅에 도움이 됩니다.

    public boolean isTokenValid(String token) {
        try {
            getClaims(token);
            return true;
-        } catch (Exception e) {
+        } catch (io.jsonwebtoken.ExpiredJwtException e) {
+            // 토큰 만료
+            return false;
+        } catch (io.jsonwebtoken.MalformedJwtException e) {
+            // 잘못된 토큰 형식
+            return false;
+        } catch (io.jsonwebtoken.UnsupportedJwtException e) {
+            // 지원하지 않는 토큰
+            return false;
+        } catch (io.jsonwebtoken.security.SecurityException e) {
+            // 서명 검증 실패
+            return false;
+        } catch (Exception e) {
+            // 기타 예외
             return false;
         }
     }
src/main/java/com/example/FixLog/exception/GlobalExceptionHandler.java (2)

10-16: 메서드명이 부정확합니다.

메서드명이 handleDuplicateException인데 실제로는 모든 CustomException을 처리합니다. 더 일반적인 이름으로 변경하는 것이 좋습니다.

-    protected ResponseEntity<Response<?>> handleDuplicateException(CustomException ex) {
+    protected ResponseEntity<Response<?>> handleCustomException(CustomException ex) {

8-17: 추가 예외 처리를 고려해보세요.

현재는 CustomException만 처리하고 있습니다. 일반적인 예외들(IllegalArgumentException, NullPointerException 등)에 대한 처리도 추가하면 더 안전한 애플리케이션이 됩니다.

다음과 같은 핸들러들을 추가하는 것을 고려해보세요:

@ExceptionHandler(IllegalArgumentException.class)
protected ResponseEntity<Response<?>> handleIllegalArgumentException(IllegalArgumentException ex) {
    logger.error("IllegalArgumentException occurred", ex);
    return new ResponseEntity<>(Response.fail("잘못된 요청입니다."), HttpStatus.BAD_REQUEST);
}

@ExceptionHandler(Exception.class)
protected ResponseEntity<Response<?>> handleGeneralException(Exception ex) {
    logger.error("Unexpected error occurred", ex);
    return new ResponseEntity<>(Response.fail("서버 오류가 발생했습니다."), HttpStatus.INTERNAL_SERVER_ERROR);
}
src/main/java/com/example/FixLog/dto/Response.java (1)

19-25: 추가 편의 메서드를 고려해보세요.

현재 구현도 충분하지만, 데이터 없이 성공 응답을 반환하는 경우를 위한 오버로드 메서드가 있으면 더 편리할 것입니다.

     public static <T> Response<T> success(String message, T data) {
         return new Response<>(true, message, data);
     }
+
+    public static <T> Response<T> success(String message) {
+        return new Response<>(true, message, null);
+    }
src/main/java/com/example/FixLog/mock/BookmarkFolderTestDataInitializer.java (1)

22-33: 테스트 데이터 초기화 로직이 잘 구현되어 있습니다.

@Order(2) 어노테이션으로 멤버 초기화 후 실행되도록 순서를 관리하고, ifPresentOrElse를 사용한 에러 처리도 적절합니다. 다만 현재 조건문은 전체 북마크 폴더가 비어있는지만 확인하는데, 특정 사용자의 폴더 존재 여부를 확인하는 것이 더 정확할 것 같습니다.

더 정확한 조건 검사를 위해 다음과 같이 수정하는 것을 고려해보세요:

    @Override
    public void run(String... args) {
-        if (bookmarkFolderRepository.count() == 0) {
            memberRepository.findByEmail("[email protected]").ifPresentOrElse(member -> {
+                if (bookmarkFolderRepository.findByUserId(member).isEmpty()) {
                    BookmarkFolder defaultFolder = new BookmarkFolder(member, "default folder");
                    BookmarkFolder etcFolder = new BookmarkFolder(member, "그외 폴더");
                    bookmarkFolderRepository.saveAll(List.of(defaultFolder, etcFolder));
                    System.out.println("테스트용 북마크 폴더 2개 생성 완료");
+                }
            }, () -> {
                System.out.println("[email protected] 사용자가 존재하지 않아 폴더 생성 생략됨");
            });
-        }

이렇게 하면 해당 사용자의 폴더만 체크하여 중복 생성을 방지할 수 있습니다.

src/main/java/com/example/FixLog/dto/post/PostDto.java (1)

8-22: PostDto 클래스 설계 개선 제안

현재 구현은 기능적으로 문제없으나 다음 개선사항을 고려해보세요:

  1. 빌더 패턴 적용: 22개의 필드를 가진 생성자는 가독성이 떨어집니다.
  2. 필드 검증: 필수 필드(예: postTitle)에 대한 null 체크나 빈 문자열 검증 고려
  3. 불변성 강화: 현재 List tags는 외부에서 수정 가능합니다.

다음과 같이 개선할 수 있습니다:

+import lombok.Builder;
+import java.util.Collections;

 @Getter
-@AllArgsConstructor
+@Builder
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
 public class PostDto {
     private String postTitle;
     // ... other fields
-    private List<String> tags;
+    private List<String> tags;
+    
+    public List<String> getTags() {
+        return tags != null ? Collections.unmodifiableList(tags) : Collections.emptyList();
+    }
 }
src/main/java/com/example/FixLog/domain/bookmark/BookmarkFolder.java (1)

25-27: 필드명과 실제 타입의 일관성을 개선해주세요.

userId 필드명이 실제로는 Member 객체를 참조하고 있어 혼란을 줄 수 있습니다. 의도를 더 명확히 하기 위해 필드명을 변경하는 것을 고려해보세요.

-@OneToOne(fetch = FetchType.LAZY)
-@JoinColumn(name = "user_id", nullable = false)
-private Member userId;
+@OneToOne(fetch = FetchType.LAZY)
+@JoinColumn(name = "user_id", nullable = false)  
+private Member member;

또는

-@OneToOne(fetch = FetchType.LAZY)
-@JoinColumn(name = "user_id", nullable = false)
-private Member userId;
+@OneToOne(fetch = FetchType.LAZY)
+@JoinColumn(name = "user_id", nullable = false)
+private Member owner;
src/main/java/com/example/FixLog/service/MemberService.java (1)

46-48: 비즈니스 로직 분리를 고려하세요.

회원가입 시 기본 북마크 폴더 생성 로직이 MemberService에 포함되어 있어 단일 책임 원칙(SRP)에 위배됩니다.

이 로직을 별도 서비스나 이벤트 기반으로 분리하는 것을 고려해보세요:

// 예시: ApplicationEventPublisher 사용
@EventListener
public void handleMemberRegistered(MemberRegisteredEvent event) {
    BookmarkFolder newFolder = new BookmarkFolder(event.getMember());
    bookmarkFolderRepository.save(newFolder);
}
src/main/java/com/example/FixLog/service/AuthService.java (1)

22-44: 예외 처리와 로깅 개선

인증 실패 시 보안을 위해 더 일반적인 에러 메시지를 사용하고, 로깅을 추가하는 것을 고려하세요.

@Slf4j // 클래스 레벨에 추가
public class AuthService {
    
    public LoginResponseDto login(LoginRequestDto requestDto) {
        try {
            Member member = memberRepository.findByEmail(requestDto.getEmail())
                    .orElseThrow(() -> new CustomException(ErrorCode.INVALID_CREDENTIALS));

            if (!passwordEncoder.matches(requestDto.getPassword(), member.getPassword())) {
                log.warn("Failed login attempt for email: {}", requestDto.getEmail());
                throw new CustomException(ErrorCode.INVALID_CREDENTIALS);
            }
            
            log.info("Successful login for user: {}", member.getEmail());
            // ... 나머지 로직
        } catch (Exception e) {
            log.error("Login error for email: {}", requestDto.getEmail(), e);
            throw e;
        }
    }
}
src/main/java/com/example/FixLog/service/MainPageService.java (2)

54-59: 매직 넘버 사용 및 에러 처리 개선 필요

sort 파라미터 검증에서 매직 넘버(0, 1)를 사용하고 있습니다. enum을 사용하여 가독성과 유지보수성을 향상시키는 것을 권장합니다.

다음과 같이 개선할 수 있습니다:

+public enum SortType {
+    LATEST(0), POPULAR(1);
+    
+    private final int value;
+    SortType(int value) { this.value = value; }
+    public int getValue() { return value; }
+    
+    public static SortType fromValue(int value) {
+        return Arrays.stream(values())
+                .filter(type -> type.value == value)
+                .findFirst()
+                .orElseThrow(() -> new CustomException(ErrorCode.SORT_NOT_EXIST));
+    }
+}

-        if (sort == 0) { // 최신순 정렬
-            posts = postRepository.findTop12ByOrderByCreatedAtDesc();
-        } else if (sort == 1) { // 인기순 정렬
-            posts = postRepository.findTop12ByOrderByPostLikesDesc();
-        } else
-            throw new CustomException(ErrorCode.SORT_NOT_EXIST);
+        SortType sortType = SortType.fromValue(sort);
+        posts = switch (sortType) {
+            case LATEST -> postRepository.findTop12ByOrderByCreatedAtDesc();
+            case POPULAR -> postRepository.findTop12ByOrderByPostLikesDesc();
+        };

85-94: 매직 넘버 하드코딩 문제

페이지 크기(12)와 sort 검증 로직이 하드코딩되어 있습니다. 상수로 정의하거나 설정 파일로 분리하는 것을 권장합니다.

+    private static final int DEFAULT_PAGE_SIZE = 12;

-        Pageable pageable = PageRequest.of(page - 1, 12);
+        Pageable pageable = PageRequest.of(page - 1, DEFAULT_PAGE_SIZE);
src/main/java/com/example/FixLog/config/JwtAuthenticationFilter.java (1)

49-55: 토큰 추출 로직 보안 강화 권장

Bearer 토큰 추출 로직이 적절하게 구현되어 있습니다. 추가적인 보안 강화를 위해 토큰 길이 검증을 고려해볼 수 있습니다.

private String resolveToken(HttpServletRequest request) {
    String bearerToken = request.getHeader("Authorization");
    if (StringUtils.hasText(bearerToken) && bearerToken.startsWith("Bearer ")) {
-        return bearerToken.substring(7);
+        String token = bearerToken.substring(7);
+        return token.length() > 0 ? token : null;
    }
    return null;
}
src/main/java/com/example/FixLog/service/BookmarkFolderService.java (3)

43-43: 페이지 크기 하드코딩 문제

페이지 크기가 10으로 하드코딩되어 있습니다. 상수로 정의하거나 설정 파일로 분리하는 것을 권장합니다.

+    private static final int DEFAULT_FOLDER_PAGE_SIZE = 10;

-        Pageable pageable = PageRequest.of(page - 1, 10); // 기본 size = 10
+        Pageable pageable = PageRequest.of(page - 1, DEFAULT_FOLDER_PAGE_SIZE);

93-96: 불필요한 빈 줄 제거 필요

파일 끝에 불필요한 빈 줄들이 있습니다. 코드 포맷팅을 정리해주세요.

        bookmarkFolderRepository.delete(folder);
    }
-



}
+}

69-71: 소유권 검증 로직 중복

폴더 수정과 삭제에서 동일한 소유권 검증 로직이 반복됩니다. 공통 메서드로 추출하는 것을 권장합니다.

+    private void validateFolderOwnership(BookmarkFolder folder, Member member) {
+        if (!folder.getUserId().equals(member)) {
+            throw new CustomException(ErrorCode.ACCESS_DENIED);
+        }
+    }

    public void updateFolderName(Long folderId, String email, String newName) {
        Member member = getMemberByEmailOrThrow(email);
        BookmarkFolder folder = bookmarkFolderRepository.findById(folderId)
                .orElseThrow(() -> new CustomException(ErrorCode.FOLDER_NOT_FOUND));
-        // 본인만 수정 가능
-        if (!folder.getUserId().equals(member)) {
-            throw new CustomException(ErrorCode.ACCESS_DENIED);
-        }
+        validateFolderOwnership(folder, member);

Also applies to: 86-88

src/main/java/com/example/FixLog/dto/main/MainPageResponseDto.java (1)

18-19: JsonInclude 어노테이션 사용법 검증

totalPages 필드에 @JsonInclude(JsonInclude.Include.NON_DEFAULT)를 사용했는데, int 타입의 기본값은 0이므로 의도한 대로 동작할 것입니다. 하지만 명시적으로 @JsonInclude(JsonInclude.Include.NON_ZERO)를 사용하는 것이 더 명확할 수 있습니다.

-    @JsonInclude(JsonInclude.Include.NON_DEFAULT) // totalPage = 0 인 경우에는 출력되지 않도록
+    @JsonInclude(JsonInclude.Include.NON_ZERO) // totalPages = 0 인 경우에는 출력되지 않도록
    private int totalPages;
src/main/java/com/example/FixLog/dto/post/PostRequestDto.java (1)

23-24: 주석 처리된 코드 정리 권장

주석 처리된 필드들이 향후 확장 계획을 나타내지만, 코드 가독성을 위해 TODO 주석으로 대체하거나 별도 문서로 관리하는 것을 권장합니다.

-//    private List<String> postImageUrl;
-//    private List<String> tags;
+// TODO: 향후 구현 예정 기능
+// - 다중 이미지 업로드 (postImageUrl)
+// - 문자열 기반 태그 지원 (tags)
src/main/java/com/example/FixLog/domain/post/Post.java (1)

35-62: @lob 어노테이션 사용 검토

모든 텍스트 필드에 @Lob 어노테이션을 사용했는데, 이는 성능에 영향을 줄 수 있습니다. 실제 데이터 크기에 따라 적절한 길이 제한을 고려해보세요.

-@Lob
-@Column(nullable = false)
+@Column(length = 1000, nullable = false)
 private String problem;

-@Lob
-@Column(nullable = false)
+@Column(length = 500, nullable = false)
 private String errorMessage;

정말 큰 텍스트가 예상되는 필드에만 @Lob을 사용하고, 나머지는 적절한 길이 제한을 설정하는 것을 권장합니다.

src/main/java/com/example/FixLog/controller/MainPageController.java (2)

14-16: 의존성 주입 어노테이션 누락

AuthController에서는 @RequiredArgsConstructor를 사용했지만, 여기서는 수동으로 생성자를 작성했습니다. 일관성을 위해 Lombok 어노테이션 사용을 권장합니다.

+import lombok.RequiredArgsConstructor;
+
 @RestController
 @RequestMapping("/main")
+@RequiredArgsConstructor
 public class MainPageController {
     private final MainPageService mainPageService;
-
-    public MainPageController(MainPageService mainPageService){
-        this.mainPageService = mainPageService;
-    }

19-19: 변수명 오타 수정

userIddto 변수명에 오타가 있습니다. 일관된 네이밍 컨벤션을 적용하세요.

-public Response<Object> mainPageView(@RequestParam("sort") int sort, @RequestBody UserIdDto userIddto){
+public Response<Object> mainPageView(@RequestParam("sort") int sort, @RequestBody UserIdDto userIdDto){

26행의 동일한 오타도 함께 수정해주세요.

src/main/java/com/example/FixLog/domain/member/Member.java (2)

73-75: 중복된 주석 제거 필요

같은 내용의 주석이 반복되고 있습니다.

-    // Member 객체를 정적 팩토리 방식으로 회원가입 시에 생성하는 메서드
-    // Member 객체를 정적 팩토리 방식으로 생성하는 메서드
     // Creates a Member object using a static factory method

83-83: 하드코딩된 프로필 이미지 URL 개선 필요

기본 프로필 이미지 URL이 하드코딩되어 있습니다. 설정 파일이나 상수로 관리하는 것이 좋습니다.

설정 파일로 관리하는 방식으로 리팩토링하시겠습니까? 구현을 도와드릴 수 있습니다.

src/main/java/com/example/FixLog/controller/PostController.java (1)

11-17: Lombok을 사용하여 코드 일관성 유지

다른 서비스 클래스들과 일관성을 위해 Lombok의 @RequiredArgsConstructor를 사용하는 것이 좋습니다.

+import lombok.RequiredArgsConstructor;

 @RestController
 @RequestMapping("/posts")
+@RequiredArgsConstructor
 public class PostController {
     private final PostService postService;
-
-    public PostController(PostService postService){
-        this.postService = postService;
-    }
src/main/java/com/example/FixLog/controller/FollowController.java (1)

26-26: 임시 인증 방식 사용 확인

JWT 구현 전까지 requesterEmail 파라미터를 사용하는 것으로 이해했습니다. 추후 @AuthenticationPrincipal로 변경 시 모든 엔드포인트를 일괄 업데이트해야 합니다.

JWT 인증 구현 관련 이슈를 생성하여 추적하시겠습니까?

Also applies to: 36-36, 45-45, 54-54

src/main/java/com/example/FixLog/controller/BookmarkFolderController.java (1)

75-77: 불필요한 빈 줄을 제거하세요.

파일 끝에 불필요한 빈 줄들이 있습니다.

 }
-

-
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a796682 and 99478c7.

📒 Files selected for processing (67)
  • .coderabbit.yaml (1 hunks)
  • build.gradle (1 hunks)
  • src/main/java/com/example/FixLog/config/JwtAuthenticationFilter.java (1 hunks)
  • src/main/java/com/example/FixLog/config/SecurityConfig.java (1 hunks)
  • src/main/java/com/example/FixLog/controller/AuthController.java (1 hunks)
  • src/main/java/com/example/FixLog/controller/BookmarkFolderController.java (1 hunks)
  • src/main/java/com/example/FixLog/controller/FollowController.java (1 hunks)
  • src/main/java/com/example/FixLog/controller/MainPageController.java (1 hunks)
  • src/main/java/com/example/FixLog/controller/MemberController.java (1 hunks)
  • src/main/java/com/example/FixLog/controller/PostController.java (1 hunks)
  • src/main/java/com/example/FixLog/domain/bookmark/Bookmark.java (1 hunks)
  • src/main/java/com/example/FixLog/domain/bookmark/BookmarkFolder.java (1 hunks)
  • src/main/java/com/example/FixLog/domain/follow/Follow.java (1 hunks)
  • src/main/java/com/example/FixLog/domain/fork/Fork.java (1 hunks)
  • src/main/java/com/example/FixLog/domain/like/PostLike.java (1 hunks)
  • src/main/java/com/example/FixLog/domain/member/Member.java (1 hunks)
  • src/main/java/com/example/FixLog/domain/member/SocialType.java (1 hunks)
  • src/main/java/com/example/FixLog/domain/post/Post.java (1 hunks)
  • src/main/java/com/example/FixLog/domain/post/PostTag.java (1 hunks)
  • src/main/java/com/example/FixLog/domain/tag/Tag.java (1 hunks)
  • src/main/java/com/example/FixLog/domain/tag/TagCategory.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/Response.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/UserIdDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/bookmark/request/BookmarkFolderCreateRequest.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/bookmark/request/BookmarkFolderUpdateRequest.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/bookmark/request/BookmarkMoveRequest.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/bookmark/response/BookmarkFolderCreateResponse.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/bookmark/response/BookmarkFolderPageResponse.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/bookmark/response/BookmarkFolderReadResponse.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/follow/request/FollowRequestDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/follow/request/UnfollowRequestDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/follow/response/FollowResponseDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/follow/response/FollowerListResponseDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/follow/response/FollowingListResponseDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/main/MainPagePostResponseDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/main/MainPageResponseDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/member/DuplicateCheckResponseDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/member/LoginRequestDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/member/LoginResponseDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/member/MemberInfoResponseDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/member/SignupRequestDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/post/PostDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/post/PostRequestDto.java (1 hunks)
  • src/main/java/com/example/FixLog/dto/post/PostResponseDto.java (1 hunks)
  • src/main/java/com/example/FixLog/exception/CustomException.java (1 hunks)
  • src/main/java/com/example/FixLog/exception/ErrorCode.java (1 hunks)
  • src/main/java/com/example/FixLog/exception/GlobalExceptionHandler.java (1 hunks)
  • src/main/java/com/example/FixLog/mock/BookmarkFolderTestDataInitializer.java (1 hunks)
  • src/main/java/com/example/FixLog/mock/MemberTestDataInitializer.java (1 hunks)
  • src/main/java/com/example/FixLog/mock/TagTestDataInitializer.java (1 hunks)
  • src/main/java/com/example/FixLog/repository/MemberRepository.java (1 hunks)
  • src/main/java/com/example/FixLog/repository/bookmark/BookmarkFolderRepository.java (1 hunks)
  • src/main/java/com/example/FixLog/repository/bookmark/BookmarkRepository.java (1 hunks)
  • src/main/java/com/example/FixLog/repository/follow/FollowRepository.java (1 hunks)
  • src/main/java/com/example/FixLog/repository/like/PostLikeRepository.java (1 hunks)
  • src/main/java/com/example/FixLog/repository/post/PostRepository.java (1 hunks)
  • src/main/java/com/example/FixLog/repository/post/PostTagRepository.java (1 hunks)
  • src/main/java/com/example/FixLog/repository/tag/TagRepository.java (1 hunks)
  • src/main/java/com/example/FixLog/service/AuthService.java (1 hunks)
  • src/main/java/com/example/FixLog/service/BookmarkFolderService.java (1 hunks)
  • src/main/java/com/example/FixLog/service/BookmarkService.java (1 hunks)
  • src/main/java/com/example/FixLog/service/FollowService.java (1 hunks)
  • src/main/java/com/example/FixLog/service/MainPageService.java (1 hunks)
  • src/main/java/com/example/FixLog/service/MemberService.java (1 hunks)
  • src/main/java/com/example/FixLog/service/PostService.java (1 hunks)
  • src/main/java/com/example/FixLog/util/JwtUtil.java (1 hunks)
  • src/main/resources/application.properties (1 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (15)
src/main/java/com/example/FixLog/dto/follow/request/UnfollowRequestDto.java (1)
src/main/java/com/example/FixLog/dto/follow/request/FollowRequestDto.java (1)
  • Getter (7-12)
src/main/java/com/example/FixLog/dto/member/LoginResponseDto.java (2)
src/main/java/com/example/FixLog/dto/Response.java (1)
  • JsonInclude (6-26)
src/main/java/com/example/FixLog/dto/member/LoginRequestDto.java (1)
  • Getter (5-9)
src/main/java/com/example/FixLog/dto/member/SignupRequestDto.java (1)
src/main/java/com/example/FixLog/dto/member/LoginRequestDto.java (1)
  • Getter (5-9)
src/main/java/com/example/FixLog/mock/BookmarkFolderTestDataInitializer.java (2)
src/main/java/com/example/FixLog/mock/MemberTestDataInitializer.java (1)
  • Component (16-40)
src/main/java/com/example/FixLog/mock/TagTestDataInitializer.java (1)
  • Component (13-30)
src/main/java/com/example/FixLog/dto/follow/response/FollowResponseDto.java (2)
src/main/java/com/example/FixLog/dto/follow/response/FollowerListResponseDto.java (1)
  • Getter (7-17)
src/main/java/com/example/FixLog/dto/follow/response/FollowingListResponseDto.java (1)
  • Getter (7-17)
src/main/java/com/example/FixLog/domain/post/PostTag.java (2)
src/main/java/com/example/FixLog/domain/post/Post.java (1)
  • Entity (15-100)
src/main/java/com/example/FixLog/domain/tag/Tag.java (1)
  • Entity (8-30)
src/main/java/com/example/FixLog/domain/bookmark/BookmarkFolder.java (2)
src/main/java/com/example/FixLog/domain/bookmark/Bookmark.java (1)
  • Entity (11-44)
src/main/java/com/example/FixLog/domain/member/Member.java (1)
  • Entity (21-120)
src/main/java/com/example/FixLog/controller/MainPageController.java (1)
src/main/java/com/example/FixLog/controller/PostController.java (1)
  • RestController (10-45)
src/main/java/com/example/FixLog/dto/UserIdDto.java (5)
src/main/java/com/example/FixLog/dto/follow/request/FollowRequestDto.java (1)
  • Getter (7-12)
src/main/java/com/example/FixLog/dto/follow/request/UnfollowRequestDto.java (1)
  • Getter (7-12)
src/main/java/com/example/FixLog/dto/member/LoginRequestDto.java (1)
  • Getter (5-9)
src/main/java/com/example/FixLog/dto/member/SignupRequestDto.java (1)
  • Getter (6-13)
src/main/java/com/example/FixLog/dto/post/PostRequestDto.java (1)
  • Getter (7-25)
src/main/java/com/example/FixLog/dto/post/PostDto.java (1)
src/main/java/com/example/FixLog/dto/post/PostResponseDto.java (1)
  • Getter (8-17)
src/main/java/com/example/FixLog/dto/main/MainPagePostResponseDto.java (1)
src/main/java/com/example/FixLog/dto/main/MainPageResponseDto.java (1)
  • Getter (10-25)
src/main/java/com/example/FixLog/dto/follow/request/FollowRequestDto.java (1)
src/main/java/com/example/FixLog/dto/follow/request/UnfollowRequestDto.java (1)
  • Getter (7-12)
src/main/java/com/example/FixLog/service/MainPageService.java (1)
src/main/java/com/example/FixLog/service/PostService.java (1)
  • Service (31-187)
src/main/java/com/example/FixLog/dto/follow/response/FollowerListResponseDto.java (2)
src/main/java/com/example/FixLog/dto/follow/response/FollowResponseDto.java (1)
  • Getter (7-17)
src/main/java/com/example/FixLog/dto/follow/response/FollowingListResponseDto.java (1)
  • Getter (7-17)
src/main/java/com/example/FixLog/dto/main/MainPageResponseDto.java (1)
src/main/java/com/example/FixLog/dto/main/MainPagePostResponseDto.java (1)
  • Getter (9-19)
🪛 YAMLlint (1.37.1)
.coderabbit.yaml

[error] 15-15: no new line character at the end of file

(new-line-at-end-of-file)

🔇 Additional comments (40)
.coderabbit.yaml (7)

2-2: 언어 설정 확인
ISO 639-1 확장 코드 "ko-KR"이 스키마에 부합하며, PR 요구사항인 한국어 리뷰와 일치합니다.


3-3: early_access 설정 확인
early_access: false로 조기 접근 기능이 비활성화되어 있어 의도된 기본 설정과 일치합니다.


5-5: 리뷰 프로필 설정 확인
profile: "chill" 설정으로 과도한 피드백을 피하는 모드가 활성화되어 있습니다. 의도에 부합합니다.


6-6: 자동 승인 워크플로우 확인 필요
request_changes_workflow: true로 설정하면 코드래빗 리뷰 완료 후 자동으로 PR이 승인됩니다. 이 동작이 팀의 리뷰 프로세스와 일치하는지 검토 부탁드립니다.


7-10: 리뷰 출력 옵션 확인
high_level_summary, poem, review_status, collapse_walkthrough 등 리뷰 출력 관련 옵션이 의도대로 설정되어 있어 보입니다.


11-13: 자동 리뷰 세부 설정 확인
auto_review.enabled: truedrafts: false로 PR 자동 리뷰가 활성화되며, 드래프트는 제외됩니다. 의도된 동작인지 확인하세요.


14-15: 채팅 자동 응답 설정 확인
chat.auto_reply: true로 설정되어 있으며, 자동 응답 기능이 활성화됩니다. 의도에 부합합니다.

🧰 Tools
🪛 YAMLlint (1.37.1)

[error] 15-15: no new line character at the end of file

(new-line-at-end-of-file)

src/main/java/com/example/FixLog/repository/MemberRepository.java (1)

8-11: 표준적인 JPA 리포지토리 구현이 올바릅니다.

Spring Data JPA의 메서드 네이밍 규칙을 잘 따르고 있으며, Optional 반환 타입을 사용하여 null 안전성을 확보했습니다. 이메일과 닉네임으로 회원을 조회하는 기본적인 기능이 잘 구현되어 있습니다.

src/main/java/com/example/FixLog/domain/member/SocialType.java (1)

3-6: 소셜 로그인 타입 열거형이 잘 구현되었습니다.

간단하고 명확한 enum 구현입니다. 향후 다른 소셜 로그인 제공자(예: GOOGLE, KAKAO 등)를 추가하기 용이한 구조입니다.

src/main/java/com/example/FixLog/repository/tag/TagRepository.java (1)

1-7: 표준 JPA 리포지토리 구현이 잘 되어 있습니다.

Spring Data JPA의 표준 패턴을 올바르게 따르고 있으며, 기본적인 CRUD 및 페이지네이션 기능을 제공합니다.

src/main/java/com/example/FixLog/repository/post/PostTagRepository.java (1)

1-7: Post와 Tag 간의 다대다 관계를 위한 리포지토리가 올바르게 구현되었습니다.

PostTag 엔티티를 위한 표준 JPA 리포지토리 패턴을 잘 따르고 있습니다.

src/main/java/com/example/FixLog/dto/member/LoginResponseDto.java (1)

7-10: 응답 DTO 구현 양호
@JsonInclude(JsonInclude.Include.NON_NULL)@AllArgsConstructor를 통해 null 필드 제외 및 생성자 주입이 잘 구성되었습니다.

build.gradle (1)

37-39:

Details

✅ Verification successful

spring-security-test 제거 영향 검증 요청
보안 관련 테스트에서 @WithMockUser 등 스프링 시큐리티 테스트 지원을 사용 중이라면 컴파일/실행 오류가 발생할 수 있습니다. 관련 의존성 제거 전후 테스트 통과 여부를 확인하세요.

다음 스크립트를 실행하여 보안 테스트 의존성을 참조하는 코드를 찾아보세요:


🏁 Script executed:

#!/bin/bash
# Verify no Spring Security test annotations are used without dependency
rg -n "@WithMockUser" --glob="src/test/java/**/*.java"

Length of output: 54


추가 검증을 위해 다음 스크립트를 실행해 보세요.
테스트 코드 전체에서 Spring Security 테스트 어노테이션 및 관련 임포트를 검색합니다.


🏁 Script executed:

#!/bin/bash
# 1. @WithMockUser, @WithUserDetails, @WithAnonymousUser 어노테이션 검색
rg -n "@WithMockUser"
rg -n "@WithUserDetails"
rg -n "@WithAnonymousUser"
# 2. Spring Security 테스트 의존성 임포트 검색
rg -n "import.*org.springframework.security.test"

Length of output: 125


spring-security-test 의존성 제거 검증 완료
@WithMockUser, @WithUserDetails, @WithAnonymousUserorg.springframework.security.test 임포트를 전체 테스트 코드에서 검색한 결과, 해당 어노테이션이나 관련 임포트가 전혀 발견되지 않았습니다.
따라서 spring-security-test 의존성 제거로 인한 컴파일/실행 오류는 발생하지 않습니다.

src/main/java/com/example/FixLog/dto/bookmark/request/BookmarkMoveRequest.java (2)

5-7: BookmarkMoveRequest DTO 구현 양호
Java 레코드를 활용해 간결하게 작성되었습니다.


1-4: Java 레코드 역직렬화 지원 확인 요청
Jackson이 Java 레코드 파라미터 역직렬화를 지원하도록 jackson-module-parameter-names 모듈이 등록되어 있는지 검증해주세요.

src/main/java/com/example/FixLog/dto/bookmark/request/BookmarkFolderUpdateRequest.java (2)

5-7: DTO 구현 적절
Java 레코드를 활용하여 폴더 이름 업데이트 요청을 깔끔하게 표현했습니다.


6-6: JSON 프로퍼티 네이밍 검증 요청
@JsonProperty("name")으로 매핑된 folderName 필드가 API 문서 및 프론트엔드 요청 규격과 일치하는지 확인해주세요.

src/main/java/com/example/FixLog/exception/CustomException.java (1)

8-10: 잘 구현된 커스텀 예외 클래스입니다.

표준적인 예외 처리 패턴을 잘 따르고 있습니다:

  • RuntimeException 확장으로 unchecked exception 구현
  • Lombok을 활용한 깔끔한 코드
  • ErrorCode enum을 통한 구조화된 에러 관리
src/main/java/com/example/FixLog/dto/member/DuplicateCheckResponseDto.java (1)

8-10: 코드가 잘 구현되었습니다.

간단하고 명확한 DTO 구조로 중복 검사 응답을 위한 목적에 적합합니다.

src/main/java/com/example/FixLog/dto/bookmark/response/BookmarkFolderReadResponse.java (1)

5-8: Modern Java record 사용이 훌륭합니다.

record 클래스와 @JsonProperty 어노테이션을 사용하여 깔끔하고 불변의 DTO를 구현했습니다. API 응답의 일관성을 위한 JSON 필드명 지정도 적절합니다.

src/main/java/com/example/FixLog/dto/post/PostResponseDto.java (1)

10-17: 포스트 응답 DTO가 잘 설계되었습니다.

포스트 정보와 사용자별 상태(좋아요, 북마크)를 포함한 포괄적인 응답 구조입니다. 필드명이 명확하고 타입 선택도 적절합니다.

src/main/java/com/example/FixLog/domain/tag/TagCategory.java (1)

6-9: 깔끔한 enum 구조와 한글 표시명 적용을 잘 구현했습니다.

태그 카테고리의 계층 구조(대분류, 중분류, 소분류)가 명확하게 정의되어 있고, 한글 표시명이 적절히 캡슐화되어 있습니다.

src/main/java/com/example/FixLog/dto/member/MemberInfoResponseDto.java (1)

9-15: 🛠️ Refactor suggestion

DTO 구조는 양호하나 유효성 검증과 보안 고려사항을 추가하세요.

현재 구조는 깔끔하지만 다음 개선사항을 고려해보세요:

  1. 유효성 검증: 이메일 형식, URL 형식 등에 대한 검증 어노테이션 추가
  2. 민감정보 처리: 이메일과 같은 민감한 정보의 노출 범위 검토
  3. null 안전성: 필드에 대한 null 처리 정책 명시

다음과 같은 개선을 고려해보세요:

+import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.Size;
+import com.fasterxml.jackson.annotation.JsonInclude;

+@JsonInclude(JsonInclude.Include.NON_NULL)
 public class MemberInfoResponseDto {
+    @Email(message = "올바른 이메일 형식이 아닙니다")
     private String email;
+    
+    @Size(max = 20, message = "닉네임은 20자를 초과할 수 없습니다")
     private String nickname;
+    
     private String profileImageUrl;
     private String bio;
     private SocialType socialType;

Likely an incorrect or invalid review comment.

src/main/java/com/example/FixLog/domain/tag/Tag.java (1)

8-22: JPA 엔티티 구조가 모범 사례를 잘 따르고 있습니다.

다음과 같은 좋은 설계 패턴이 적용되어 있습니다:

  • 적절한 JPA 어노테이션 사용
  • EnumType.STRING 사용으로 가독성 확보
  • 보호된 기본 생성자로 JPA 요구사항 충족
  • 컬럼 제약조건 명시
src/main/java/com/example/FixLog/util/JwtUtil.java (1)

28-36: JWT 토큰 생성 로직이 잘 구현되었습니다.

토큰에 필요한 정보(userId, email)를 적절히 포함하고, 만료 시간과 서명을 올바르게 설정했습니다. HMAC-SHA256 알고리즘 사용도 적절합니다.

src/main/java/com/example/FixLog/dto/Response.java (2)

19-25: 응답 래퍼 클래스가 잘 설계되었습니다.

제네릭을 활용한 타입 안전성, 정적 팩토리 메서드를 통한 명확한 생성 방식, Jackson 어노테이션을 통한 적절한 JSON 직렬화 설정이 모두 잘 구현되어 있습니다.


6-6: JSON 직렬화 설정이 적절합니다.

@JsonInclude(JsonInclude.Include.NON_NULL) 어노테이션을 통해 null 값을 응답에서 제외하는 것이 API 응답 크기를 줄이고 클라이언트 측 처리를 단순화하는 좋은 설계입니다.

src/main/java/com/example/FixLog/dto/bookmark/response/BookmarkFolderPageResponse.java (1)

7-13: 페이지네이션 응답 DTO 구현이 잘 되어 있습니다.

표준적인 페이지네이션 응답 패턴을 잘 따르고 있으며, Jackson 어노테이션을 통한 JSON 필드 매핑도 적절합니다. Record 클래스를 사용한 것도 불변 객체로서 DTO에 적합한 선택입니다.

src/main/java/com/example/FixLog/dto/main/MainPagePostResponseDto.java (1)

9-19: DTO 구조가 잘 설계되어 있습니다.

메인 페이지에 필요한 포스트 정보를 효율적으로 담고 있으며, LocalDate 사용에 대한 주석도 의도를 명확히 설명하고 있어 좋습니다. Lombok 어노테이션 사용으로 보일러플레이트 코드도 적절히 줄였습니다.

src/main/java/com/example/FixLog/repository/follow/FollowRepository.java (1)

10-15: 팔로우 기능에 필요한 메서드들이 잘 정의되어 있습니다.

중복 팔로우 방지, 팔로우 관계 조회, 팔로워/팔로잉 목록 조회 등 핵심 기능에 필요한 메서드들이 Spring Data JPA 네이밍 규칙을 따라 명확하게 정의되어 있습니다. 각 메서드의 목적을 설명하는 한국어 주석도 도움이 됩니다.

src/main/java/com/example/FixLog/domain/bookmark/BookmarkFolder.java (1)

37-45: 생성자 오버로딩이 잘 구현되어 있습니다.

사용자 지정 폴더명과 기본 폴더명("default folder")을 위한 두 가지 생성자가 적절히 제공되어 있어, 다양한 사용 시나리오를 지원할 수 있습니다.

src/main/java/com/example/FixLog/domain/bookmark/Bookmark.java (1)

35-39: 🛠️ Refactor suggestion

생성자 매개변수명을 필드명과 일치시키세요.

위에서 제안한 필드명 변경에 따라 생성자도 함께 수정이 필요합니다.

필드명 변경 후 생성자도 다음과 같이 수정하세요:

-    public Bookmark(BookmarkFolder folderId, Post postId){
-        this.folderId = folderId;
-        this.postId = postId;
+    public Bookmark(BookmarkFolder folder, Post post){
+        this.folder = folder;
+        this.post = post;
         this.isMarked = true; // 객체 생성 시 true
     }

Likely an incorrect or invalid review comment.

src/main/java/com/example/FixLog/config/SecurityConfig.java (1)

30-34: PR 목표와 일치하지 않는 API 경로 설정

PR 요약에서는 "/api" 접두사를 제거한다고 했으나, SecurityConfig에서는 여전히 "/api" 경로를 사용하고 있습니다.

다음 스크립트로 전체 코드베이스에서 API 경로 사용 현황을 확인해보세요:

#!/bin/bash
# Description: API 경로 패턴 확인
# Expected: "/api" 접두사 사용 현황 파악

echo "=== Controller에서 /api 경로 사용 현황 ==="
rg -A 3 -B 1 "RequestMapping.*api|PostMapping.*api|GetMapping.*api|PutMapping.*api|DeleteMapping.*api"

echo "=== SecurityConfig에서 /api 경로 사용 현황 ==="
rg -A 2 -B 2 "requestMatchers.*api"

Likely an incorrect or invalid review comment.

src/main/java/com/example/FixLog/config/JwtAuthenticationFilter.java (1)

37-44: JWT 토큰 검증 및 인증 로직 검토

JWT 토큰 검증과 인증 설정이 적절히 구현되어 있습니다. MEMBER_NOT_FOUND 에러 코드 사용은 PR 목표인 에러 메시지 통합과 일치합니다.

src/main/java/com/example/FixLog/dto/main/MainPageResponseDto.java (1)

10-25: 잘 구현된 DTO 클래스

DTO 구조가 깔끔하게 설계되었습니다. @JsonInclude 어노테이션을 사용하여 조건부 직렬화를 구현한 것과, 두 가지 생성자를 제공하여 유연성을 확보한 것이 좋습니다.

src/main/java/com/example/FixLog/service/FollowService.java (2)

25-47: 팔로우 기능이 잘 구현되었습니다!

자기 자신 팔로우 방지와 중복 팔로우 체크가 적절히 구현되어 있습니다.


69-84: 읽기 전용 트랜잭션 설정이 적절합니다!

조회 메서드에 @Transactional(readOnly = true)를 사용하여 성능 최적화를 고려한 점이 좋습니다.

src/main/java/com/example/FixLog/controller/BookmarkFolderController.java (3)

1-13: 임포트 구문이 적절합니다.

필요한 의존성들이 올바르게 임포트되어 있고, 사용되지 않는 임포트는 없습니다.


15-20: 컨트롤러 클래스 선언이 적절합니다.

@RestController, @RequiredArgsConstructor, @RequestMapping 어노테이션이 올바르게 사용되었고, 의존성 주입도 적절합니다.


66-74: 폴더 삭제 로직이 적절합니다.

삭제 기능이 올바르게 구현되어 있습니다. 다만 다른 메서드들과 마찬가지로 인증 방식 개선을 고려해보세요.

Copy link
Contributor

@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.

Review continued from previous batch...

…SER_NICKNAME_NOT_FOUND로 변경 후 로그인시 회원정보 없을 때 USER_NICKNAME_NOT_FOUND로 변경
@sungchaewon sungchaewon requested review from semsemin and youngseo22 and removed request for semsemin and youngseo22 May 28, 2025 15:13
@sungchaewon sungchaewon changed the base branch from main to develop May 28, 2025 15:14
@youngseo22 youngseo22 marked this pull request as ready for review May 28, 2025 15:17
@sungchaewon sungchaewon merged commit 16d7242 into develop May 28, 2025
1 check passed
@sungchaewon sungchaewon deleted the feature/#41-delete-member-cw branch May 28, 2025 16:14
@coderabbitai coderabbitai bot mentioned this pull request Jun 23, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

del 쓸모없는 코드나 파일 삭제

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEAT] 회원탈퇴

4 participants