Skip to content

Feat/#14 [Folder] 폴더 도메인 추가 및 CRUD 구현#15

Merged
jin2304 merged 14 commits intodevfrom
feat/#14
Jan 17, 2026
Merged

Feat/#14 [Folder] 폴더 도메인 추가 및 CRUD 구현#15
jin2304 merged 14 commits intodevfrom
feat/#14

Conversation

@jin2304
Copy link
Member

@jin2304 jin2304 commented Jan 3, 2026

💡 이슈

resolve {#14}

🤩 개요

  • 폴더 도메인 및 CRUD API 구현

🧑‍💻 작업 사항

폴더 API 구현

  • POST /api/folders: 폴더 생성
  • GET /api/folders/{folderId}: 폴더 단일 조회
  • GET /api/folders: 폴더 목록 조회 (태그 필터링, 정렬 지원)
  • PUT /api/folders/{folderId}: 폴더 수정
  • DELETE /api/folders/{folderId}: 폴더 삭제
  • GET /api/folders/tags: 폴더 태그 목록 조회

📖 참고 사항

공유할 내용, 레퍼런스, 추가로 발생할 것으로 예상되는 이슈, 스크린샷 등을 넣어 주세요.

Summary by CodeRabbit

릴리스 노트

  • 새로운 기능

    • 북마크를 폴더로 체계적으로 구성하고 관리할 수 있는 폴더 관리 기능 추가
    • 폴더별 북마크 필터링 및 검색 기능 향상
    • 폴더 태그 기반 조회 및 관리 기능 추가
  • 개선 사항

    • 폴더 도메인 추가에 따른 북마크 조회 API 수정
      • 북마크 검색 및 필터링 기능 통합으로 더욱 유연한 검색 경험 제공

✏️ Tip: You can customize this high-level summary in your review settings.

jin2304 added 10 commits January 1, 2026 23:07
- FolderService 인터페이스 정의
- FolderServiceImpl 구현
  - 폴더 생성/조회/수정/삭제 비즈니스 로직 추가
- 북마크 목록 조회
  - folderId 파라미터 추가
  - 기존 분기 방식을 단일 selectBookmarkList()로 통합

- 북마크 태그 조회
  - folderId 파라미터 추가
- 북마크 목록 조회
  - folderId 파라미터 추가
  - 기존 분기 방식을 단일 selectBookmarkList()로 통합

- 북마크 태그 조회
  - folderId 파라미터 추가
- 북마크 목록 조회
  - folderId 파라미터 추가
  - 기존 분기 방식을 단일 selectBookmarkList()로 통합

- 북마크 추가/수정/태그 조회
  - folderId 파라미터 추가
@jin2304 jin2304 added the ✨ feat 새로운 기능을 추가 label Jan 3, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 3, 2026

Walkthrough

이 풀 리퀘스트는 북마크 검색 기능을 단일 통합 메서드로 리팩토링하고 폴더 관리 기능을 새로 추가합니다. 북마크 서비스의 selectBookmarkList(), selectBookmarkListByTag(), selectBookmarkListByQuery() 세 메서드를 BookmarkSearchRequestDto를 매개변수로 받는 하나의 메서드로 통합합니다. 폴더 모듈은 전체 CRUD 작업을 수행하는 컨트롤러, 서비스, DAO 계층을 포함하여 신규 추가되었습니다. 북마크와 폴더 테이블에 폴더 필터링을 지원하기 위해 folder_folderId 필드가 추가되며, MyBatis 매퍼 파일이 새로운 검색 로직과 폴더 관련 쿼리를 포함하도록 업데이트됩니다.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant MyPageController
    participant BookmarkService
    participant BookmarkDao
    participant Database
    
    Client->>MyPageController: getBookmarks(memberId, tag, sort, query, folderId)
    activate MyPageController
    
    MyPageController->>BookmarkService: selectBookmarkList(memberId, tag, sort, query, folderId)
    activate BookmarkService
    
    Note over BookmarkService: BookmarkSearchRequestDto 생성
    BookmarkService->>BookmarkDao: selectBookmarkList(searchRequest)
    activate BookmarkDao
    
    BookmarkDao->>Database: SELECT * FROM bookmark<br/>WHERE memberId=? AND folderId=?<br/>AND tag=? AND name LIKE ?<br/>ORDER BY created_date
    activate Database
    
    Database-->>BookmarkDao: 북마크 목록
    deactivate Database
    
    BookmarkDao-->>BookmarkService: List<Bookmark>
    deactivate BookmarkDao
    
    BookmarkService-->>MyPageController: List<BookmarkDto>
    deactivate BookmarkService
    
    MyPageController-->>Client: ResponseEntity<List<BookmarkDto>>
    deactivate MyPageController
Loading
sequenceDiagram
    participant Client
    participant FolderController
    participant FolderService
    participant FolderDao
    participant Database
    
    Client->>FolderController: POST /myPage/{memberId}/folder
    activate FolderController
    
    FolderController->>FolderService: insertFolder(memberId, folderCreateRequestDto)
    activate FolderService
    
    Note over FolderService: Folder 객체 생성
    FolderService->>FolderDao: insertFolder(folder)
    activate FolderDao
    
    FolderDao->>Database: INSERT INTO folder<br/>(member_memberId, name, tag, created_date)
    activate Database
    
    Database-->>FolderDao: folderId (generated key)
    deactivate Database
    
    FolderDao-->>FolderService: folderId
    deactivate FolderDao
    
    FolderService-->>FolderController: FolderResponseDto
    deactivate FolderService
    
    FolderController-->>Client: ResponseEntity<FolderResponseDto>
    deactivate FolderController
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Poem

🐰 검색은 하나로, 폴더는 새것이라
데이터 모델 깔끔하게 정렬하고
필터 조건 유연하게 담아낸
우리의 북마크, 더욱 똑똑해졌네 🎉
다중 경로 하나로 모아서
서비스 계층 한결 단순해졌고
인생도 폴더처럼 정리정돈!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (2 warnings)
Check name Status Explanation Resolution
Description check ⚠️ Warning PR 설명이 템플릿 구조만 있을 뿐 모든 섹션이 미완성 상태입니다. 개요와 작업 사항이 작성되지 않았고, 실제 변경 내용이 설명되지 않았습니다. 모든 필수 섹션을 채워주세요: 개요 섹션에서 PR의 목적 설명, 작업 사항 섹션에서 구체적인 변경 내용 및 폴더 관리 기능 추가와 북마크 API 리팩토링 세부 사항을 작성하세요.
Docstring Coverage ⚠️ Warning Docstring coverage is 73.21% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed 제목이 PR의 주요 변경사항을 명확하게 설명하고 있습니다. 폴더 CRUD 기능 추가 및 관련 수정사항을 간결하게 표현했습니다.

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

✨ Finishing touches
  • 📝 Generate docstrings

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: 15

📜 Review details

Configuration used: Repository UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d5c759c and 3d126d2.

📒 Files selected for processing (23)
  • src/main/java/com/web/SearchWeb/bookmark/controller/BookmarkApiController.java
  • src/main/java/com/web/SearchWeb/bookmark/dao/BookmarkDao.java
  • src/main/java/com/web/SearchWeb/bookmark/dao/MybatisBookmarkDao.java
  • src/main/java/com/web/SearchWeb/bookmark/domain/Bookmark.java
  • src/main/java/com/web/SearchWeb/bookmark/dto/BookmarkDto.java
  • src/main/java/com/web/SearchWeb/bookmark/dto/request/BookmarkSearchRequestDto.java
  • src/main/java/com/web/SearchWeb/bookmark/service/BookmarkService.java
  • src/main/java/com/web/SearchWeb/bookmark/service/BookmarkServiceImpl.java
  • src/main/java/com/web/SearchWeb/folder/controller/FolderController.java
  • src/main/java/com/web/SearchWeb/folder/dao/FolderDao.java
  • src/main/java/com/web/SearchWeb/folder/dao/MybatisFolderDao.java
  • src/main/java/com/web/SearchWeb/folder/domain/Folder.java
  • src/main/java/com/web/SearchWeb/folder/dto/request/FolderCreateRequestDto.java
  • src/main/java/com/web/SearchWeb/folder/dto/request/FolderSearchRequestDto.java
  • src/main/java/com/web/SearchWeb/folder/dto/request/FolderUpdateRequestDto.java
  • src/main/java/com/web/SearchWeb/folder/dto/response/FolderResponseDto.java
  • src/main/java/com/web/SearchWeb/folder/service/FolderService.java
  • src/main/java/com/web/SearchWeb/folder/service/FolderServiceImpl.java
  • src/main/java/com/web/SearchWeb/mypage/controller/MyPageController.java
  • src/main/resources/application.properties
  • src/main/resources/db/init.sql
  • src/main/resources/mapper/bookmark-mapper.xml
  • src/main/resources/mapper/folder-mapper.xml
🧰 Additional context used
🧬 Code graph analysis (4)
src/main/java/com/web/SearchWeb/folder/dto/request/FolderUpdateRequestDto.java (4)
src/main/java/com/web/SearchWeb/folder/domain/Folder.java (1)
  • Getter (9-21)
src/main/java/com/web/SearchWeb/folder/dto/request/FolderSearchRequestDto.java (1)
  • Getter (8-16)
src/main/java/com/web/SearchWeb/folder/dto/response/FolderResponseDto.java (1)
  • Getter (13-52)
src/main/java/com/web/SearchWeb/folder/dto/request/FolderCreateRequestDto.java (1)
  • Setter (9-17)
src/main/java/com/web/SearchWeb/bookmark/dto/request/BookmarkSearchRequestDto.java (1)
src/main/java/com/web/SearchWeb/bookmark/domain/Bookmark.java (1)
  • Getter (8-22)
src/main/java/com/web/SearchWeb/folder/dto/response/FolderResponseDto.java (2)
src/main/java/com/web/SearchWeb/folder/domain/Folder.java (1)
  • Getter (9-21)
src/main/java/com/web/SearchWeb/folder/dto/request/FolderSearchRequestDto.java (1)
  • Getter (8-16)
src/main/java/com/web/SearchWeb/folder/domain/Folder.java (2)
src/main/java/com/web/SearchWeb/folder/dto/request/FolderSearchRequestDto.java (1)
  • Getter (8-16)
src/main/java/com/web/SearchWeb/folder/dto/response/FolderResponseDto.java (1)
  • Getter (13-52)
🔇 Additional comments (35)
src/main/resources/application.properties (1)

3-4: ✓ 환경 변수를 통한 Spring 프로필 선택 설정이 적절합니다.

${SPRING_PROFILES_ACTIVE} 플레이스홀더를 사용하여 런타임 시 활성 프로필을 환경 변수로부터 동적으로 선택할 수 있는 깔끔한 구현입니다. 이는 개발/스테이징/프로덕션 환경 간 설정 관리의 표준 방식입니다.

src/main/java/com/web/SearchWeb/folder/dto/request/FolderUpdateRequestDto.java (1)

9-17: DTO 구조가 FolderCreateRequestDto와 동일합니다.

현재 FolderUpdateRequestDtoFolderCreateRequestDto가 동일한 필드 구조를 가지고 있습니다. 생성과 수정의 관심사 분리를 위해 분리한 것으로 보이며, 이는 유효한 설계입니다. 다만 향후 필드가 달라질 가능성이 없다면 공통 베이스 DTO를 고려해볼 수 있습니다.

src/main/java/com/web/SearchWeb/folder/dto/request/FolderCreateRequestDto.java (1)

9-17: 폴더 생성 DTO 구조가 적절합니다.

DTO 구조가 명확하고 폴더 생성에 필요한 필드를 적절하게 포함하고 있습니다.

src/main/java/com/web/SearchWeb/folder/dto/response/FolderResponseDto.java (2)

28-39: LGTM! null 안전성이 잘 구현되어 있습니다.

단일 엔티티 매핑 메서드가 올바르게 구현되어 있습니다. null 체크와 빌더 패턴 사용이 적절합니다.

참고: member_memberId 필드가 응답 DTO에 포함되지 않는 것은 보안상 의도된 설계로 보입니다.


44-51: LGTM! Stream API를 활용한 깔끔한 구현입니다.

리스트 매핑 메서드가 올바르게 구현되어 있습니다. null 체크와 Stream API 사용이 적절하며, 메서드 참조를 통한 단일 엔티티 매퍼 재사용이 효율적입니다.

src/main/java/com/web/SearchWeb/bookmark/service/BookmarkServiceImpl.java (3)

9-9: LGTM! 필요한 import가 추가되었습니다.

새로운 DTO 기반 검색을 위해 필요한 import입니다.


56-65: LGTM! DTO 기반 통합 검색이 올바르게 구현되었습니다.

여러 검색 조건을 단일 DTO로 통합한 리팩토링이 적절합니다. 모든 매개변수가 올바르게 DTO에 매핑되어 있으며, 코드의 일관성과 유지보수성이 향상되었습니다.


145-147: LGTM! 폴더 컨텍스트를 반영한 확장이 적절합니다.

태그 조회 메서드에 folderId 매개변수가 추가되어 폴더별 태그 필터링을 지원합니다. DAO 레이어로의 위임이 올바르게 구현되어 있습니다.

src/main/java/com/web/SearchWeb/folder/dao/FolderDao.java (1)

8-21: LGTM! 잘 설계된 DAO 인터페이스입니다.

폴더 CRUD 작업을 위한 인터페이스가 명확하고 일관성 있게 정의되어 있습니다:

  • 메서드 이름이 명확하고 규칙을 따릅니다
  • selectFolderList에서 DTO 패턴을 사용하여 BookmarkDao와 일관성을 유지합니다
  • memberIdfolderId 조합을 통한 권한 범위 제어가 적절합니다
  • 태그 조회 기능이 포함되어 있어 완전한 기능을 제공합니다
src/main/java/com/web/SearchWeb/mypage/controller/MyPageController.java (2)

87-94: LGTM! 폴더 필터링 기능이 올바르게 통합되었습니다.

folderId 파라미터가 선택적(required = false)으로 적절히 추가되었고, 서비스 메서드 호출이 새로운 시그니처와 일치합니다. 기존 API의 하위 호환성이 유지됩니다.


100-106: LGTM! 태그 조회 API에 폴더 범위 필터링이 추가되었습니다.

folderId 파라미터 추가로 특정 폴더 내 태그만 조회할 수 있게 되었습니다. 선택적 파라미터로 기존 동작과의 호환성이 유지됩니다.

src/main/java/com/web/SearchWeb/bookmark/controller/BookmarkApiController.java (1)

84-87: LGTM! 통합된 메서드 시그니처로 올바르게 마이그레이션되었습니다.

기존 2개 파라미터에서 5개 파라미터로 변경되었으며, 필터링이 필요 없는 파라미터에 null을 전달하여 기존 동작을 유지합니다.

src/main/java/com/web/SearchWeb/bookmark/dao/BookmarkDao.java (2)

19-20: LGTM! DTO 기반 검색 메서드로 리팩토링되었습니다.

여러 개의 개별 검색 메서드를 BookmarkSearchRequestDto를 받는 단일 메서드로 통합한 것은 좋은 설계입니다. 향후 검색 조건 추가 시 메서드 시그니처 변경 없이 DTO만 확장하면 됩니다.


38-38: LGTM! 폴더 범위 태그 조회 지원이 추가되었습니다.

folderIdLong 타입으로 정의하여 null일 경우 전체 폴더, 값이 있을 경우 특정 폴더로 필터링하는 로직이 가능합니다.

src/main/java/com/web/SearchWeb/folder/dao/MybatisFolderDao.java (2)

11-19: LGTM! 표준 MyBatis DAO 패턴을 따르고 있습니다.

SqlSession을 통해 mapper를 획득하고 위임하는 패턴이 기존 MybatisBookmarkDao와 일관성 있게 구현되었습니다.


22-56: LGTM! CRUD 및 태그 조회 메서드가 올바르게 위임되었습니다.

모든 메서드가 mapper에 단순 위임하며 DAO 레이어에 비즈니스 로직이 포함되지 않아 적절합니다.

src/main/java/com/web/SearchWeb/bookmark/service/BookmarkService.java (2)

20-21: LGTM! 통합된 검색 메서드 시그니처입니다.

기존 3개의 분리된 메서드(selectBookmarkList, selectBookmarkListByTag, selectBookmarkListByQuery)를 5개 파라미터를 받는 단일 메서드로 통합하여 API 표면적을 줄였습니다.


38-39: LGTM!

폴더 범위 태그 조회를 위한 파라미터 추가가 적절합니다.

src/main/java/com/web/SearchWeb/folder/service/FolderServiceImpl.java (3)

14-22: LGTM! 서비스 클래스 구조가 적절합니다.

생성자 주입 방식으로 FolderDao 의존성을 주입받으며, @Service 어노테이션으로 Spring 빈 등록이 올바르게 설정되었습니다.


38-54: LGTM! 조회 메서드가 적절히 구현되었습니다.

도메인 객체를 응답 DTO로 변환하는 로직이 서비스 레이어에 위치하여 계층 분리가 잘 되어 있습니다. FolderSearchRequestDto 빌더 패턴 사용도 적절합니다.


57-78: LGTM! 수정/삭제/태그조회 메서드가 올바르게 구현되었습니다.

DAO에 위임하는 패턴이 일관되게 적용되었습니다.

src/main/java/com/web/SearchWeb/folder/service/FolderService.java (1)

9-22: LGTM! 깔끔한 인터페이스 정의입니다.

폴더 CRUD 및 태그 조회 기능이 명확한 메서드 시그니처로 정의되었습니다. 요청/응답 DTO 분리와 일관된 네이밍 컨벤션이 적용되었습니다.

src/main/resources/mapper/folder-mapper.xml (2)

24-47: LGTM!

selectFolderList 쿼리의 동적 SQL 구현이 적절합니다. 태그 필터링과 정렬 옵션이 올바르게 처리되며, otherwise 절이 기본값을 제공합니다.


8-12: LGTM!

useGeneratedKeyskeyProperty 설정이 올바르게 구성되어 생성된 폴더 ID를 도메인 객체에 반환합니다.

src/main/java/com/web/SearchWeb/folder/controller/FolderController.java (2)

61-80: LGTM!

조회 엔드포인트들이 REST 규칙을 잘 따르고 있으며, 선택적 파라미터에 적절한 기본값이 설정되어 있습니다.


99-116: LGTM!

삭제 및 태그 조회 엔드포인트가 올바르게 구현되었습니다. @OwnerCheck AOP를 통한 권한 검증이 일관되게 적용되어 있습니다.

src/main/java/com/web/SearchWeb/bookmark/dao/MybatisBookmarkDao.java (2)

27-33: LGTM!

selectBookmarkList 메서드가 DTO 기반 접근 방식으로 깔끔하게 리팩토링되었습니다. 여러 개별 메서드를 하나의 통합 메서드로 통합한 것은 코드 유지보수성을 향상시킵니다.


137-140: LGTM!

selectTags 메서드에 folderId 파라미터(nullable Long 타입)가 추가되어 폴더별 태그 필터링을 지원합니다.

src/main/resources/mapper/bookmark-mapper.xml (3)

37-50: LGTM!

통합된 selectBookmarkList 쿼리의 동적 필터링 로직이 올바르게 구현되었습니다. folderId, tag, query 조건이 적절한 null 체크와 함께 처리됩니다. 대소문자 구분 없는 검색을 위한 LOWER() 함수 사용도 적절합니다.


63-101: LGTM!

모든 INSERT 및 UPDATE 문에 folder_folderId 필드가 일관되게 추가되었습니다. 폴더 연관 기능을 위한 스키마 변경과 잘 맞습니다.


141-151: LGTM!

selectTags 쿼리가 폴더별 태그 조회를 지원하도록 올바르게 확장되었습니다. folderId에 대한 조건부 필터링이 적절합니다.

src/main/resources/db/init.sql (4)

145-147: comment 테이블의 데이터 중복으로 인한 정합성 문제 가능성이 있습니다.

member 테이블에 이미 존재하는 nickname, job, major를 comment 테이블에 중복 저장하고 있습니다.

멤버가 프로필을 업데이트하면:

  • member 테이블은 새 정보를 반영
  • 기존 comment 레코드는 구 정보를 유지 (불일치 발생)

이것이 의도된 설계인지 확인이 필요합니다:

  • 의도적: 댓글 작성 당시의 멤버 정보를 역사적으로 보존하려는 경우
  • 비의도적: member 테이블과 JOIN하여 최신 정보를 가져오는 것이 더 적합

의도하지 않은 경우, 해당 컬럼을 제거하고 애플리케이션 레벨에서 JOIN을 통해 멤버 정보를 조회하는 것을 권장합니다.


80-90: folder 테이블 구조가 적절합니다.

새로 추가된 folder 테이블의 설계가 잘 되어 있습니다:

  • AUTO_INCREMENT를 사용한 적절한 PK 설정
  • member와의 FK 관계에 ON DELETE CASCADE 사용 (멤버 삭제 시 폴더도 삭제)
  • modified_date에 ON UPDATE CURRENT_TIMESTAMP를 적용하여 자동 업데이트
  • member_memberId에 인덱스 설정으로 조회 성능 최적화

102-102: bookmark 테이블의 folder 통합이 적절히 구현되었습니다.

폴더 기능이 bookmark 테이블에 올바르게 통합되었습니다:

  • folder_folderId가 nullable로 설정되어 폴더 없는 북마크도 지원
  • ON DELETE SET NULL로 폴더 삭제 시 북마크는 보존되며 폴더 참조만 해제
  • 인덱스 추가로 폴더별 북마크 조회 성능 최적화

Also applies to: 112-112, 116-116


24-24: website 테이블은 애플리케이션에서 읽기 전용으로 사용되는 참조 데이터 테이블입니다. 데이터베이스 초기화 시 미리 등록되며, INSERT/UPDATE/DELETE 작업이 없으므로 websiteId의 AUTO_INCREMENT 부재는 설계상 문제가 아닙니다.

private int member_memberId;
private int website_websiteId;
private int board_boardId;
private int folder_folderId;
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

BookmarkDto와 타입 불일치를 수정하세요.

Bookmark.folder_folderIdint 타입이지만, BookmarkDto.folder_folderIdLong 타입입니다. 이 타입 불일치는 도메인-DTO 간 매핑 시 문제를 발생시킬 수 있습니다.

또한 primitive int는 null 값을 가질 수 없어 폴더가 할당되지 않은 상태를 표현하기 어렵습니다. Integer 타입 사용을 권장합니다.

필드명도 Java 명명 규칙에 맞게 folderId로 변경하는 것을 권장합니다.

🔎 제안하는 수정 사항
-    private int folder_folderId;
+    private Integer folderId;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private int folder_folderId;
private Integer folderId;
🤖 Prompt for AI Agents
In src/main/java/com/web/SearchWeb/bookmark/domain/Bookmark.java around line 16,
the field declaration uses a primitive and non-idiomatic name: "private int
folder_folderId"; change this to a nullable wrapper with a proper Java name to
match the DTO by renaming it to "private Long folderId" (or, if you prefer
Integer across layers, update DTO accordingly) and update the corresponding
getter/setter, any constructors, equals/hashCode, and mapping code to use the
new name and type so domain and BookmarkDto types and names are consistent and
null can be represented.

private int member_memberId;
private int website_websiteId;
private int board_boardId;
private Long folder_folderId;
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

필드 이름을 Java 명명 규칙에 맞게 수정하세요.

folder_folderId는 snake_case를 사용하고 있어 Java 명명 규칙에 맞지 않습니다. folderId로 변경하는 것을 권장합니다.

또한 Bookmark 도메인 엔티티와의 타입 불일치(int vs Long)가 있습니다. Bookmark.java에서 해당 필드를 Integer로 변경하면 일관성을 유지할 수 있습니다.

🔎 제안하는 수정 사항
-    private Long folder_folderId;
+    private Long folderId;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private Long folder_folderId;
private Long folderId;
🤖 Prompt for AI Agents
In src/main/java/com/web/SearchWeb/bookmark/dto/BookmarkDto.java around line 16,
rename the field folder_folderId to folderId to follow Java naming conventions
and make the DTO and entity types consistent by using Integer for the folderId
across both layers: change the DTO field type from Long to Integer (and update
its getters/setters, constructors, builders, and any JSON/property annotations),
and update the Bookmark domain entity to use Integer as well (or vice versa if
you prefer Long—just ensure both DTO and entity match); then update all usages,
mappings, and tests that reference the old name or type.

Comment on lines +12 to +18
public class BookmarkSearchRequestDto {
private int memberId;
private String tag;
private String sort;
private String query;
private Long folderId;
} No newline at end of file
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

ID 타입 일관성을 검토하세요.

memberId는 primitive int로, folderId는 wrapper Long으로 선언되어 타입 일관성이 부족합니다.

folderIdLong인 이유는 null을 허용하여 "폴더 필터 없음"을 표현하기 위함으로 보입니다. 이것이 의도된 설계라면 문제없지만, 참고로 Bookmark 도메인에서는 folder_folderIdint로 선언되어 있어 타입 불일치가 발생합니다.

향후 유지보수를 위해 ID 타입 전략(primitive vs wrapper, int vs Long)을 프로젝트 전체적으로 일관되게 가져가는 것을 권장합니다.

🤖 Prompt for AI Agents
In
src/main/java/com/web/SearchWeb/bookmark/dto/request/BookmarkSearchRequestDto.java
around lines 12-18, the ID types are inconsistent (memberId is primitive int
while folderId is Long); change both ID fields to the same wrapper type (use
Integer for memberId and folderId instead of int/Long) to allow nullability and
maintain consistency with Bookmark domain types, then update any callers,
mappers or domain mappings to use Integer as the canonical ID type (or, if the
project decision is to use Long, change both to Long and align domain/repository
accordingly).

Comment on lines +86 to +93
@PutMapping("/myPage/{memberId}/folder/{folderId}")
@OwnerCheck(idParam = "memberId", service = "memberService")
public ResponseEntity<Integer> updateFolder(@PathVariable final int memberId,
@PathVariable final int folderId,
@RequestBody FolderUpdateRequestDto folderUpdateRequestDto) {
int result = folderService.updateFolder(memberId, folderId, folderUpdateRequestDto);
return ResponseEntity.ok(result);
}
Copy link

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

@Valid 어노테이션 추가 필요

updateFolder@RequestBody에도 @Valid 어노테이션을 추가하여 입력 유효성 검증을 수행하세요.

🔎 제안된 수정
     public ResponseEntity<Integer> updateFolder(@PathVariable final int memberId,
                                                 @PathVariable final int folderId,
-                                                @RequestBody FolderUpdateRequestDto folderUpdateRequestDto) {
+                                                @RequestBody @Valid FolderUpdateRequestDto folderUpdateRequestDto) {
🤖 Prompt for AI Agents
In src/main/java/com/web/SearchWeb/folder/controller/FolderController.java
around lines 86 to 93, the @RequestBody parameter lacks validation; add the
@Valid annotation to the FolderUpdateRequestDto parameter (i.e., change the
method signature to accept @Valid @RequestBody FolderUpdateRequestDto) and
import the appropriate javax.validation.Valid or jakarta.validation.Valid
depending on your project, so Spring will perform bean validation on the
incoming payload.

PRIMARY KEY (`boardId`),
KEY `memberId_idx` (`member_memberId`),
KEY `idx_created_date` (`created_date` DESC),
CONSTRAINT `memberId` FOREIGN KEY (`member_memberId`) REFERENCES `member` (`memberId`)
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

외래 키 ON DELETE 동작이 일관되지 않습니다.

member 삭제 시 연관 데이터 처리가 테이블마다 다릅니다:

  • folder: ON DELETE CASCADE (멤버 삭제 시 폴더도 삭제)
  • board, bookmark, likes, likebookmark, comment: ON DELETE 지정 없음 (기본값 RESTRICT)

이로 인해:

  • 멤버 삭제 시 일부 테이블은 삭제 차단 (likes, board 등에 데이터가 있으면 삭제 실패)
  • 데이터 정합성 관리가 복잡해짐
  • 애플리케이션 레벨에서 추가 처리 필요

비즈니스 요구사항에 따라 일관된 전략을 수립해야 합니다:

  1. 멤버 삭제 시 모든 관련 데이터 삭제: 모든 FK에 ON DELETE CASCADE
  2. 멤버 삭제 차단: 명시적으로 ON DELETE RESTRICT (또는 생략)
  3. 멤버 삭제 허용하되 데이터 보존: ON DELETE SET NULL (nullable 컬럼 필요)

현재 bookmark -> folder는 ON DELETE SET NULL을 적절히 사용하고 있습니다.

Also applies to: 73-73, 89-89, 113-116, 133-134, 153-154

🤖 Prompt for AI Agents
In src/main/resources/db/init.sql around lines 56 (and also lines 73, 89,
113-116, 133-134, 153-154), several FOREIGN KEY constraints lack an explicit ON
DELETE action leading to inconsistent behavior across tables; choose the desired
member-deletion strategy and apply it consistently: add ON DELETE CASCADE to all
member-related FKs if you want related rows removed when a member is deleted, or
ON DELETE RESTRICT if you want deletions blocked, or ON DELETE SET NULL (and
make the FK column nullable) if you want to preserve rows while nulling the
owner; update each FK definition accordingly and adjust column
nullability/indexes where SET NULL is chosen so the schema stays valid.

Comment on lines 51 to 60
ORDER BY
<choose>
<when test="sort == 'Oldest'">
modified_date ASC
</when>
<when test="sort == 'Newest'">
modified_date DESC
</when>
</choose>
</select>


<!-- 북마크 목록 조회 (시간, 태그, 검색어) -->
<select id="selectBookmarkListByQuery" parameterType="map" resultType="com.web.SearchWeb.bookmark.domain.Bookmark">
select *
from bookmark
WHERE member_memberId=#{memberId}
<if test="tag != 'All'">
AND tag = #{tag}
</if>
AND
LOWER(name) LIKE CONCAT('%', LOWER(#{query}), '%')
ORDER BY
<choose>
<when test="sort == 'Oldest'">
modified_date ASC
</when>
<when test="sort == 'Newest'">
modified_date DESC
</when>
</choose>
</select>
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

ORDER BY 절에 기본값 누락 - SQL 구문 오류 가능

sort 값이 'Newest' 또는 'Oldest'가 아닌 경우 <otherwise> 절이 없어 SQL 구문 오류가 발생합니다. folder-mapper.xmlselectFolderList처럼 기본값을 추가해야 합니다.

🔎 제안된 수정
         ORDER BY
         <choose>
             <when test="sort == 'Newest'">
                 modified_date DESC
             </when>
             <when test="sort == 'Oldest'">
                 modified_date ASC
             </when>
+            <otherwise>
+                modified_date DESC
+            </otherwise>
         </choose>
🤖 Prompt for AI Agents
In src/main/resources/mapper/bookmark-mapper.xml around lines 51 to 60, the
ORDER BY choose block lacks an <otherwise> branch so when sort is neither
'Newest' nor 'Oldest' the SQL becomes invalid; add an <otherwise> clause that
provides a sensible default ordering (e.g., modified_date DESC or the same
default used in folder-mapper.xml) so the generated SQL always contains a valid
ORDER BY.

Comment on lines +60 to +65
<!-- 폴더 삭제 -->
<delete id="deleteFolder" parameterType="map">
DELETE FROM folder
WHERE folderId = #{folderId}
AND member_memberId = #{memberId}
</delete>
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

🧩 Analysis chain

🏁 Script executed:

# Search for FolderDao interface and implementation files
fd -e java | xargs grep -l "deleteFolder" | head -20

Repository: Searchweb-Dev/Searchweb

Length of output: 397


🏁 Script executed:

# Search for the deleteFolder method definition and its usage
rg "deleteFolder" -A 3 -B 1 --type java

Repository: Searchweb-Dev/Searchweb

Length of output: 2910


🏁 Script executed:

# Also check the mapper file to see all parameter names used
cat src/main/resources/mapper/folder-mapper.xml

Repository: Searchweb-Dev/Searchweb

Length of output: 2369


🏁 Script executed:

cat src/main/java/com/web/SearchWeb/folder/dao/MybatisFolderDao.java

Repository: Searchweb-Dev/Searchweb

Length of output: 1427


파라미터 바인딩 오류 - 런타임 실패 발생

deleteFolderselectFolder 메서드의 파라미터 바인딩이 실패합니다. DAO 인터페이스에서 int memberId, int folderId 처럼 여러 기본형 파라미터를 사용하면서 @Param 애너테이션이 없으면, MyBatis는 자동생성된 이름(param1, param2 등)을 사용합니다. 그러나 mapper XML은 parameterType="map"으로 설정되어 #{memberId}, #{folderId}를 기대하므로, 런타임 시 파라미터를 찾을 수 없는 오류가 발생합니다.

FolderDao 인터페이스의 두 메서드 모두에 @Param 애너테이션을 추가하거나, Map 객체나 DTO를 파라미터로 사용하도록 수정하세요.

🤖 Prompt for AI Agents
In src/main/resources/mapper/folder-mapper.xml around lines 60 to 65, the mapper
expects named parameters #{memberId} and #{folderId} but the DAO passes multiple
primitive params without @Param so MyBatis cannot bind them; fix by updating the
FolderDao method signatures for deleteFolder and selectFolder to annotate
parameters with @Param("memberId") and @Param("folderId") respectively (or
alternatively change the DAO to accept a Map/DTO with those field names),
ensuring the XML parameterType aligns with the chosen approach so #{memberId}
and #{folderId} resolve at runtime.

- 모든 테이블 문자 셋을 utf8mb4로 통일
- 멤버 삭제 시 외래 키 동작 하이브리드 전략 적용
  - folder, bookmark, likes, likebookmark: ON DELETE CASCADE
  - board, comment: ON DELETE SET NULL (작성자 익명화)
- AUTO_INCREMENT 시작값 제거 (초기화 스크립트 정리)
@jin2304 jin2304 changed the title Feat/#14 Feat/#14 [Folder] 폴더 crud 및 그에 따른 수정 Jan 17, 2026
@jin2304 jin2304 changed the title Feat/#14 [Folder] 폴더 crud 및 그에 따른 수정 Feat/#14 [Folder] 폴더 도메인 추가 및 CRUD 구현 Jan 17, 2026
@jin2304 jin2304 merged commit 3a91912 into dev Jan 17, 2026
2 checks passed
@jin2304 jin2304 self-assigned this Jan 17, 2026
@jin2304 jin2304 deleted the feat/#14 branch February 1, 2026 07:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

✨ feat 새로운 기능을 추가

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant