Skip to content

[Fix] 모집 여부 카테고리에 정보, 자유 카테고리 글이 포함되는 오류 수정 #117

Open
echae0111 wants to merge 16 commits intodevelopfrom
fix/#116-category-fix
Open

[Fix] 모집 여부 카테고리에 정보, 자유 카테고리 글이 포함되는 오류 수정 #117
echae0111 wants to merge 16 commits intodevelopfrom
fix/#116-category-fix

Conversation

@echae0111
Copy link
Collaborator

@echae0111 echae0111 commented Mar 2, 2026

⚙️ Related ISSUE Number

closed #116



📄 Work Description



📷 Screenshot



💬 To Reviewers



🔗 Reference

<!— 문제를 해결하면서 도움이 되었거나, 참고했던 사이트 (코드링크) —>

Summary by CodeRabbit

릴리스 노트

  • API 변경

    • 모든 API 엔드포인트가 /api 접두사로 통일되었습니다.
    • 채팅, 게시물, 사용자, 순위 기능의 API 경로가 변경되었습니다.
  • 보안

    • CORS 정책 업데이트
    • WebSocket 연결 원본 제한 강화
  • 배포

    • Docker 기본 이미지 업데이트
    • 컨테이너 이미지 참조 변경
    • 환경 변수 설정 개선
  • 리팩터링

    • 게시물 필터링 로직 개선

@coderabbitai
Copy link

coderabbitai bot commented Mar 2, 2026

개요

이 PR은 API 엔드포인트에 /api 접두사 추가, 도커 이미지 및 환경 설정 업데이트, 모집 여부 카테고리 필터링 로직 개선, WebSocket CORS 설정 변경을 포함합니다. 환경 변수 및 배포 구성도 함께 수정되었습니다.

변경 사항

Cohort / File(s) Summary
API 엔드포인트 경로 표준화
src/main/java/com/devsong/server/user/controller/UserController.java, src/main/java/com/devsong/server/post/controller/PostController.java, src/main/java/com/devsong/server/ranking/controller/RankingController.java, src/main/java/com/devsong/server/chat/controller/ChatController.java
모든 컨트롤러의 @RequestMapping 경로를 /api 접두사로 표준화: /user/api/user, /post/api/post, /ranking/api/ranking. 또한 ChatController@Controller에서 @RestController로 변경.
모집 카테고리 필터링 로직
src/main/java/com/devsong/server/post/repository/PostRepository.java, src/main/java/com/devsong/server/post/service/PostService.java
리포지토리 메서드 시그니처 업데이트: findAllByClosedOrderByCreatedAtDescfindAllByClosedOrderByLikeCountDescList<Category> categories 파라미터 추가. PostService에 recruitCategories 상수(PROJECT, STUDY, EXTRA) 도입 및 카테고리 필터링 로직 적용.
보안 및 CORS 설정
src/main/java/com/devsong/server/security/SecurityConfig.java, src/main/java/com/devsong/server/chat/config/WebSocketConfig.java
SecurityConfig의 공개 API 경로를 /api/user/...로 업데이트, CORS 허용 출처를 특정 패턴에서 와일드카드(*)로 변경. WebSocketConfig의 STOMP 엔드포인트 CORS 허용 출처를 *에서 http://localhost:5173으로 제한.
도커 및 배포 설정
.gitignore, Dockerfile, docker-compose.yml
.gitignore.env 추가. Dockerfile의 기본 이미지를 openjdk:21-jdk-slim에서 eclipse-temurin:21-jdk-jammy로 변경. docker-compose의 백엔드 서비스 설정을 빌드 기반에서 이미지 참조(chaeeah/devsong-server:latest)로 변경, 데이터베이스 자격증명을 ${DB_PASSWORD} 환경 변수로 통합.

예상 코드 리뷰 난이도

🎯 2 (Simple) | ⏱️ ~12 분

관련 가능성 있는 PR

추천 리뷰어

  • chaeeah
  • Bangjh05

시 🐰

엔드포인트에 /api 옷 입혔고,
모집 카테고리로 글을 골라,
도커 이미지도 새것으로 갈아,
.env는 조용히 숨겨두고,
우리 서버는 더욱 깔끔해졌어! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning API 경로 변경(/api 접두어), Docker 이미지 업데이트, CORS 설정 변경, WebSocket 원본 제한 등 다수의 범위 외 변경사항이 포함되어 있습니다. API 경로 업데이트, Docker 설정, CORS/보안 설정 등의 변경은 별도 이슈로 분리하거나 명확히 문서화해야 합니다.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 버그 수정 내용(정보, 자유 카테고리 포함 오류 수정)을 명확히 설명하며 변경 사항과 일치합니다.
Linked Issues check ✅ Passed 변경 사항이 #116의 모집 여부 카테고리 필터링 시 정보/자유 카테고리 제외 요구사항을 충족합니다.
Docstring Coverage ✅ Passed Docstring coverage is 87.50% which is sufficient. The required threshold is 80.00%.

✏️ 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 fix/#116-category-fix

Tip

Try Coding Plans. Let us write the prompt for your AI agent so you can ship faster (with fewer bugs).
Share your feedback on Discord.


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

Caution

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

⚠️ Outside diff range comments (2)
src/main/java/com/devsong/server/chat/controller/ChatController.java (1)

46-50: ⚠️ Potential issue | 🟡 Minor

일관성을 위해 @AuthenticationPrincipal 사용으로 변경하세요.

현재 코드는 다른 컨트롤러(UserController 등)에서 광범위하게 사용 중인 @AuthenticationPrincipal Long userId 패턴과 달리 수동으로 Long.valueOf(authentication.getName())로 추출합니다. null 체크도 불필요합니다. SecurityConfig에서 해당 엔드포인트는 .authenticated()을 요구하므로 인증되지 않은 요청은 컨트롤러에 도달하기 전에 Spring Security에서 거부됩니다.

제안 수정안
+import org.springframework.security.core.annotation.AuthenticationPrincipal;
@@
     `@GetMapping`("/chat/rooms/{roomId}/messages")
     public List<ChatMessageResponseDto> getMessages(
             `@PathVariable` Long roomId,
-            Authentication authentication
+            `@AuthenticationPrincipal` Long userId
     ) {
-        if (authentication == null) {
+        if (userId == null) {
             throw new ResponseStatusException(HttpStatus.UNAUTHORIZED, "Authentication required");
         }
-        Long me = Long.valueOf(authentication.getName());
-        return chatService.getMessages(roomId, me);
+        return chatService.getMessages(roomId, userId);
     }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/java/com/devsong/server/chat/controller/ChatController.java` around
lines 46 - 50, Replace the manual Authentication handling in ChatController (the
method that currently uses Authentication authentication and
Long.valueOf(authentication.getName())) with the `@AuthenticationPrincipal` Long
userId parameter; remove the null check and the Long.valueOf conversion, and
call chatService.getMessages(roomId, userId) (or pass userId as the "me" value)
directly since the endpoint is already protected by .authenticated().
src/main/java/com/devsong/server/post/service/PostService.java (1)

125-145: ⚠️ Potential issue | 🟠 Major

모집 여부 필터에서 category가 명시되면 정보/자유가 다시 포함될 수 있습니다.

현재는 category가 비어있을 때만 recruitCategories를 강제합니다. 그래서 closed와 함께 INFO/FREE가 들어오면 기존 findAllByCategoryAndClosed... 경로로 조회되어 요구사항이 다시 깨질 수 있습니다. closed != null일 때는 명시 카테고리도 모집 카테고리 집합에 포함되는지 검사해 주세요.

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

In `@src/main/java/com/devsong/server/post/service/PostService.java` around lines
125 - 145, The category-specific branches in PostService incorrectly use
findAllByCategoryAndClosed... whenever a category string is provided, allowing
INFO/FREE to bypass the recruitCategories filter; update the logic where
Category categoryEnum = Category.from(category) is created (both in the closed
branch and the open branch) to check if closed != null and
recruitCategories.contains(categoryEnum) — if the categoryEnum is NOT in
recruitCategories, call the corresponding findAllByCategoryInAndClosed... (or
findAllByCategoryInAndClosedOrderByCreatedAtDesc / OrderByLikeCountDesc as per
sortByLike) using recruitCategories and closed; otherwise keep the current
findAllByCategoryAndClosed... path so explicit categories are only used when
they belong to recruitCategories.
🧹 Nitpick comments (3)
src/main/java/com/devsong/server/security/SecurityConfig.java (1)

50-50: setAllowCredentials(true) 중복 호출은 제거해도 됩니다.

동일 설정이 두 번 호출되고 있어 가독성만 떨어집니다. 한 번만 남기세요.

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

In `@src/main/java/com/devsong/server/security/SecurityConfig.java` at line 50,
SecurityConfig contains a duplicated call to
configuration.setAllowCredentials(true); remove the redundant invocation so the
method is only called once; locate the duplicate calls to
configuration.setAllowCredentials(true) inside the SecurityConfig class (e.g.,
in any CORS or HttpSecurity configuration methods) and delete the extra
occurrence, leaving a single call to preserve behavior and improve readability.
src/main/java/com/devsong/server/chat/config/WebSocketConfig.java (1)

29-31: 허용 Origin을 하드코딩하지 말고 설정값으로 분리해 주세요.

현재 값은 로컬 환경에는 맞지만, 스테이징/운영 프론트 도메인이 추가되면 WebSocket 연결이 바로 실패할 수 있습니다. 환경변수/프로퍼티 기반 목록으로 분리하는 게 안전합니다.

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

In `@src/main/java/com/devsong/server/chat/config/WebSocketConfig.java` around
lines 29 - 31, The WebSocketConfig currently hardcodes the allowed origin in the
call to setAllowedOriginPatterns("http://localhost:5173"); change this to read a
configurable list from application properties or environment variables (e.g.,
via `@Value`("${websocket.allowed-origins}") or a `@ConfigurationProperties` bean),
provide a sensible default that includes localhost for dev, parse the property
into a String[] or List<String>, and pass that variable into
setAllowedOriginPatterns(...) inside the same method that registers the endpoint
(the method using setAllowedOriginPatterns and
.addInterceptors(jwtHandshakeInterceptor). Ensure the property is documented in
application.yml/properties for staging/production use.
src/main/java/com/devsong/server/post/service/PostService.java (1)

104-109: recruitCategories는 상수로 올려 재사용하는 편이 좋습니다.

요청마다 리스트를 새로 만들기보다 private static final 상수로 두면 의도도 더 명확해집니다.

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

In `@src/main/java/com/devsong/server/post/service/PostService.java` around lines
104 - 109, Extract the local List<Category> recruitCategories into a class-level
constant to avoid recreating it per request: declare a private static final
List<Category> RECRUIT_CATEGORIES = List.of(Category.PROJECT, Category.STUDY,
Category.EXTRA) in the PostService class (or a shared constants holder if
preferred) and replace occurrences of the local variable recruitCategories with
RECRUIT_CATEGORIES (ensure imports and immutability are preserved).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docker-compose.yml`:
- Around line 10-11: Replace the use of the `root` DB user for the application
by adding MySQL user creation env vars and wiring them into the app config: add
`MYSQL_USER` and `MYSQL_PASSWORD` environment variables (and ensure
`MYSQL_ROOT_PASSWORD` remains set for root), then change
`SPRING_DATASOURCE_USERNAME` from `root` to `MYSQL_USER` and keep
`SPRING_DATASOURCE_PASSWORD` mapped to `MYSQL_PASSWORD`; ensure the MySQL
service/container will create that user (via these env vars) so the app runs
with a least-privilege DB account instead of root.

In `@src/main/java/com/devsong/server/security/SecurityConfig.java`:
- Around line 46-50: The CORS config uses setAllowedOriginPatterns(List.of("*"))
together with setAllowCredentials(true), which violates CORS rules; replace the
wildcard origin pattern with an explicit origin list (e.g., build origins like
"http://localhost:3000" or load allowed origins from an environment/property and
pass that list into setAllowedOrigins or setAllowedOriginPatterns) and remove
the duplicate setAllowCredentials(true) call so setAllowCredentials is only
invoked once; locate these calls on the configuration object
(setAllowedOriginPatterns, setAllowedOrigins, setAllowCredentials) and update
them to use a concrete origin list sourced from config/env and eliminate the
redundant setAllowCredentials invocation.

---

Outside diff comments:
In `@src/main/java/com/devsong/server/chat/controller/ChatController.java`:
- Around line 46-50: Replace the manual Authentication handling in
ChatController (the method that currently uses Authentication authentication and
Long.valueOf(authentication.getName())) with the `@AuthenticationPrincipal` Long
userId parameter; remove the null check and the Long.valueOf conversion, and
call chatService.getMessages(roomId, userId) (or pass userId as the "me" value)
directly since the endpoint is already protected by .authenticated().

In `@src/main/java/com/devsong/server/post/service/PostService.java`:
- Around line 125-145: The category-specific branches in PostService incorrectly
use findAllByCategoryAndClosed... whenever a category string is provided,
allowing INFO/FREE to bypass the recruitCategories filter; update the logic
where Category categoryEnum = Category.from(category) is created (both in the
closed branch and the open branch) to check if closed != null and
recruitCategories.contains(categoryEnum) — if the categoryEnum is NOT in
recruitCategories, call the corresponding findAllByCategoryInAndClosed... (or
findAllByCategoryInAndClosedOrderByCreatedAtDesc / OrderByLikeCountDesc as per
sortByLike) using recruitCategories and closed; otherwise keep the current
findAllByCategoryAndClosed... path so explicit categories are only used when
they belong to recruitCategories.

---

Nitpick comments:
In `@src/main/java/com/devsong/server/chat/config/WebSocketConfig.java`:
- Around line 29-31: The WebSocketConfig currently hardcodes the allowed origin
in the call to setAllowedOriginPatterns("http://localhost:5173"); change this to
read a configurable list from application properties or environment variables
(e.g., via `@Value`("${websocket.allowed-origins}") or a `@ConfigurationProperties`
bean), provide a sensible default that includes localhost for dev, parse the
property into a String[] or List<String>, and pass that variable into
setAllowedOriginPatterns(...) inside the same method that registers the endpoint
(the method using setAllowedOriginPatterns and
.addInterceptors(jwtHandshakeInterceptor). Ensure the property is documented in
application.yml/properties for staging/production use.

In `@src/main/java/com/devsong/server/post/service/PostService.java`:
- Around line 104-109: Extract the local List<Category> recruitCategories into a
class-level constant to avoid recreating it per request: declare a private
static final List<Category> RECRUIT_CATEGORIES = List.of(Category.PROJECT,
Category.STUDY, Category.EXTRA) in the PostService class (or a shared constants
holder if preferred) and replace occurrences of the local variable
recruitCategories with RECRUIT_CATEGORIES (ensure imports and immutability are
preserved).

In `@src/main/java/com/devsong/server/security/SecurityConfig.java`:
- Line 50: SecurityConfig contains a duplicated call to
configuration.setAllowCredentials(true); remove the redundant invocation so the
method is only called once; locate the duplicate calls to
configuration.setAllowCredentials(true) inside the SecurityConfig class (e.g.,
in any CORS or HttpSecurity configuration methods) and delete the extra
occurrence, leaving a single call to preserve behavior and improve readability.

ℹ️ Review info

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c0a3cb5 and 5f9db8d.

📒 Files selected for processing (11)
  • .gitignore
  • Dockerfile
  • docker-compose.yml
  • src/main/java/com/devsong/server/chat/config/WebSocketConfig.java
  • src/main/java/com/devsong/server/chat/controller/ChatController.java
  • src/main/java/com/devsong/server/post/controller/PostController.java
  • src/main/java/com/devsong/server/post/repository/PostRepository.java
  • src/main/java/com/devsong/server/post/service/PostService.java
  • src/main/java/com/devsong/server/ranking/controller/RankingController.java
  • src/main/java/com/devsong/server/security/SecurityConfig.java
  • src/main/java/com/devsong/server/user/controller/UserController.java

Comment on lines +10 to +11
SPRING_DATASOURCE_USERNAME: root
SPRING_DATASOURCE_PASSWORD: ${DB_PASSWORD}
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

🧩 Analysis chain

🏁 Script executed:

# First, let's look at the docker-compose.yml file structure
cat -n docker-compose.yml | head -40

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 1194


🏁 Script executed:

# Search for DB-related environment variables
rg -n 'SPRING_DATASOURCE_USERNAME|MYSQL_ROOT_PASSWORD|MYSQL_USER|MYSQL_PASSWORD|DB_PASSWORD' docker-compose.yml

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 216


🏁 Script executed:

# Look for .env files and SQL initialization scripts
fd -HI '(^\.env(\..*)?$|\.sql$)' | head -20

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 60


🏁 Script executed:

# Search for any DB user/permission initialization patterns
rg -n 'MYSQL_USER|MYSQL_PASSWORD|CREATE USER|GRANT|PRIVILEGES' --type-list

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 467


🏁 Script executed:

# Search for any entrypoint scripts or initialization files
fd -HI '(init|entrypoint|setup|schema)' --type f | grep -E '\.(sh|sql|env)$'

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 60


🏁 Script executed:

# Check if there's any documentation about DB setup
fd -HI '(README|SETUP|INSTALL|DATABASE)' --type f | head -10

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 87


🏁 Script executed:

# Look for any scripts or configs in root directory
ls -la | grep -E '\.(sh|sql|env)'

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 60


🏁 Script executed:

# Search entire codebase for CREATE USER or GRANT statements
rg -i 'CREATE USER|GRANT.*PRIVILEGES|mysql_user' --type-list --files

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 465


🏁 Script executed:

# Search entire codebase for any CREATE USER or GRANT patterns
rg 'CREATE USER|GRANT' -i

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 60


🏁 Script executed:

# Check README.md for database setup documentation
cat README.md | head -100

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 1617


🏁 Script executed:

# Search for any docker-related init scripts
fd -type f -name '*.sh' -o -name '*.sql'

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 248


애플리케이션 DB 접속 계정으로 root를 사용하는 구성은 보안 위험입니다.

최소권한 원칙(least-privilege) 위반으로, 운영 사고 시 영향 범위가 과도하게 커집니다. MySQL 서비스에 MYSQL_USERMYSQL_PASSWORD를 추가하여 앱 전용 제한된 권한 계정을 생성하고, SPRING_DATASOURCE_USERNAME도 해당 계정으로 변경해야 합니다.

MYSQL_ROOT_PASSWORD는 보안을 위해 별도로 설정하되, 애플리케이션은 제한된 권한의 전용 계정만 접근하도록 분리하세요.

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

In `@docker-compose.yml` around lines 10 - 11, Replace the use of the `root` DB
user for the application by adding MySQL user creation env vars and wiring them
into the app config: add `MYSQL_USER` and `MYSQL_PASSWORD` environment variables
(and ensure `MYSQL_ROOT_PASSWORD` remains set for root), then change
`SPRING_DATASOURCE_USERNAME` from `root` to `MYSQL_USER` and keep
`SPRING_DATASOURCE_PASSWORD` mapped to `MYSQL_PASSWORD`; ensure the MySQL
service/container will create that user (via these env vars) so the app runs
with a least-privilege DB account instead of root.

Comment on lines +46 to +50
configuration.setAllowedOriginPatterns(List.of("*"));
configuration.setAllowCredentials(true);
configuration.setAllowedMethods(List.of("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS"));
configuration.setAllowedHeaders(List.of("*"));
configuration.setAllowCredentials(true);
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Examine the specific file and lines
echo "=== Examining SecurityConfig.java ===" 
cat -n src/main/java/com/devsong/server/security/SecurityConfig.java | sed -n '40,55p'

echo -e "\n=== Searching for all CORS-related configurations in the codebase ===" 
rg -n --type=java 'setAllowedOriginPatterns|setAllowCredentials|@CrossOrigin|CorsConfiguration' src/main/java

Repository: APPS-sookmyung/2025-DEVSONG-Server

Length of output: 2594


CORS 설정에서 와일드카드와 자격증명을 함께 사용하면 안 됩니다.

setAllowedOriginPatterns(List.of("*"))setAllowCredentials(true) 조합은 CORS 표준 위반입니다. 브라우저가 이 설정을 거부합니다. 코드 41-44줄의 주석 처리된 코드처럼 명시적인 Origin 목록(예: "http://localhost:*")으로 제한하거나, 환경 변수로 관리하도록 변경해 주세요. 또한 47번 줄과 50번 줄의 setAllowCredentials(true) 호출이 중복되어 있으니 제거해 주세요.

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

In `@src/main/java/com/devsong/server/security/SecurityConfig.java` around lines
46 - 50, The CORS config uses setAllowedOriginPatterns(List.of("*")) together
with setAllowCredentials(true), which violates CORS rules; replace the wildcard
origin pattern with an explicit origin list (e.g., build origins like
"http://localhost:3000" or load allowed origins from an environment/property and
pass that list into setAllowedOrigins or setAllowedOriginPatterns) and remove
the duplicate setAllowCredentials(true) call so setAllowCredentials is only
invoked once; locate these calls on the configuration object
(setAllowedOriginPatterns, setAllowedOrigins, setAllowCredentials) and update
them to use a concrete origin list sourced from config/env and eliminate the
redundant setAllowCredentials invocation.

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.

[Fix] 모집 여부 카테고리에 정보, 자유 카테고리 글이 포함되는 오류 수정

2 participants