chore: MCP 서버 전역설치 및 심볼릭 링크 생성, 빌드타임 확인 명령어 추가#398
Conversation
WalkthroughDockerfile 업데이트로 멀티모듈 복사(cs25-entity 추가), Node.js 설치와 server-brave-search 글로벌 설치 및 심볼릭 링크 생성, 빌드 시 실행 가능 여부 검증 절차 추가, APT 정리 강화, 코멘트 경미 수정이 포함되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Dev as Developer
participant Docker as Docker Build
participant Img as Image
participant NPM as npm Registry
Dev->>Docker: Build cs25-service image
Docker->>Docker: COPY cs25-service/, cs25-entity/
Docker->>NPM: Install Node.js setup (NodeSource)
Docker->>NPM: npm i -g server-brave-search
Docker->>Docker: Create symlink to /usr/local/bin/server-brave-search
Docker->>Docker: Print npm bin path, list binary
Docker->>Docker: Run server-brave-search --help
alt Verification fails
Docker-->>Dev: Build fails (non-zero exit)
else Verification succeeds
Docker->>Img: Produce final image (cleaned APT caches)
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~15 minutes Possibly related PRs
Suggested reviewers
Poem
✨ Finishing Touches🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (2)
cs25-service/Dockerfile (2)
13-15: 주석과 실제 동작 불일치(테스트 생략 관련).주석은 “테스트 생략”이라 되어 있으나, 현재 명령은
bootJar만 실행하며 기본적으로 테스트를 수행하지 않습니다. 혼동을 줄이기 위해 주석을 정정하거나, 테스트 미실행을 명시적으로 표현해 주세요. 저는 명시적 표현을 권장합니다.적용 제안:
-# 테스트 생략하여 빌드 안정화 -RUN ./gradlew :cs25-service:bootJar --stacktrace --no-daemon +# bootJar만 실행(일반적으로 테스트 미실행). 테스트 미실행을 명시적으로 표현. +RUN ./gradlew :cs25-service:bootJar -x test --stacktrace --no-daemon
55-55: ENTRYPOINT 설정 LGTM.단일 JAR 실행에 적절합니다. 추후 보안 강화를 위해 비루트 사용자(
USER) 전환도 검토해 주세요.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
cs25-service/Dockerfile(4 hunks)
🔇 Additional comments (4)
cs25-service/Dockerfile (4)
42-46: 불필요 패키지/캐시 정리 LGTM.
npm cache clean,apt purge/autoremove/clean,rm -rf /var/lib/apt/lists/*로 이미지 슬림화가 잘 되어 있습니다.
51-52: 포트 공개 주석 정리 LGTM.
6-12: cs25-entity 모듈 설정 확인 완료
settings.gradle에서 이미include 'cs25-entity'가 선언되어 있어, 멀티모듈 복사 후 컴파일·패키징에 문제가 없습니다.
25-46: 확인 완료: server-brave-search는 런타임에 실제 호출됩니다.
- cs25-service/src/main/resources/application.properties (71행)
spring.ai.mcp.client.stdio.connections.brave.command=server-brave-search설정으로 Java 서비스가 해당 바이너리를 실행하도록 구성되어 있습니다.- 따라서 Dockerfile에 Node.js 및 server-brave-search를 포함하는 현 구성은 필수이며, 별도 사이드카나 init 컨테이너 분리는 필요하지 않습니다.
| RUN apt-get update \ | ||
| && apt-get install -y --no-install-recommends curl ca-certificates gnupg bash \ | ||
| && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ | ||
| && apt-get install -y --no-install-recommends nodejs \ | ||
| && npm install -g @modelcontextprotocol/server-brave-search \ |
There was a problem hiding this comment.
🛠️ Refactor suggestion
curl | bash(노드소스 설치) 보안/재현성 개선 필요.
- curl | bash 패턴은 보안상 바람직하지 않습니다. GPG 키링을 사용한 apt 소스 등록을 권장합니다.
- 빌드 실패 시 즉시 중단·디버깅 강화를 위해
set -euxo pipefail추가를 권장합니다. - Node 및 글로벌 패키지 버전 고정으로 재현성 확보를 권장합니다(예:
ARG로 버전 주입).
보안/재현성 개선 예시:
-RUN apt-get update \
- && apt-get install -y --no-install-recommends curl ca-certificates gnupg bash \
- && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \
- && apt-get install -y --no-install-recommends nodejs \
- && npm install -g @modelcontextprotocol/server-brave-search \
+RUN set -euxo pipefail; \
+ apt-get update \
+ && apt-get install -y --no-install-recommends curl ca-certificates gnupg bash \
+ # NodeSource GPG 키 등록 및 소스 추가(20.x 고정)
+ && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \
+ | gpg --dearmor -o /usr/share/keyrings/nodesource.gpg \
+ && echo "deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" \
+ > /etc/apt/sources.list.d/nodesource.list \
+ && apt-get update \
+ && apt-get install -y --no-install-recommends nodejs \
+ # 글로벌 패키지 버전 고정 및 불필요한 동작 비활성화
+ && npm config set fund=false audit=false \
+ && npm install -g @modelcontextprotocol/server-brave-search@${MCP_SERVER_VERSION:-latest} \추가로, Dockerfile 상단(해당 단계 시작부)에 다음 ARG를 선언하면 버전 고정이 용이합니다:
ARG MCP_SERVER_VERSION
원하시면 위 변경을 반영한 전체 RUN 블록을 정리해 드리겠습니다.
📝 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.
| RUN apt-get update \ | |
| && apt-get install -y --no-install-recommends curl ca-certificates gnupg bash \ | |
| && curl -fsSL https://deb.nodesource.com/setup_20.x | bash - \ | |
| && apt-get install -y --no-install-recommends nodejs \ | |
| && npm install -g @modelcontextprotocol/server-brave-search \ | |
| # (Ensure you have `ARG MCP_SERVER_VERSION` declared above this stage for version pinning) | |
| RUN set -euxo pipefail; \ | |
| apt-get update \ | |
| && apt-get install -y --no-install-recommends curl ca-certificates gnupg bash \ | |
| # NodeSource GPG 키 등록 및 소스 추가 (20.x 고정) | |
| && curl -fsSL https://deb.nodesource.com/gpgkey/nodesource-repo.gpg.key \ | |
| | gpg --dearmor -o /usr/share/keyrings/nodesource.gpg \ | |
| && echo "deb [signed-by=/usr/share/keyrings/nodesource.gpg] https://deb.nodesource.com/node_20.x nodistro main" \ | |
| > /etc/apt/sources.list.d/nodesource.list \ | |
| && apt-get update \ | |
| && apt-get install -y --no-install-recommends nodejs \ | |
| # 글로벌 패키지 버전 고정 및 불필요한 동작 비활성화 | |
| && npm config set fund=false audit=false \ | |
| && npm install -g @modelcontextprotocol/server-brave-search@${MCP_SERVER_VERSION:-latest} \ |
🤖 Prompt for AI Agents
In cs25-service/Dockerfile around lines 26-30, replace the insecure curl | bash
NodeSource install with adding NodeSource's APT repository using its GPG key and
signed repo entry, declare ARGs for NODE_MAJOR (or NODE_VERSION) and
MCP_SERVER_VERSION at the top and use them to pin the node and global npm
package versions, add a shell safety header (set -euxo pipefail) at the start of
the RUN step to fail fast and aid debugging, and ensure apt-get commands use
--no-install-recommends and apt-get clean && rm -rf /var/lib/apt/lists/* to keep
image small and reproducible.
| && ln -sf "$(npm bin -g)/server-brave-search" /usr/local/bin/server-brave-search \ | ||
| \ | ||
| # ===== 실행 가능 여부 확인 ===== | ||
| && echo "=== npm bin 경로 확인 ===" \ | ||
| && npm bin -g \ | ||
| && echo "=== server-brave-search 바이너리 확인 ===" \ | ||
| && ls -l "$(npm bin -g)/server-brave-search" \ | ||
| && ls -l /usr/local/bin/server-brave-search \ | ||
| && echo "=== server-brave-search --help 실행 ===" \ | ||
| && /usr/local/bin/server-brave-search --help || (echo "[ERROR] server-brave-search 실행 실패" && exit 1) \ | ||
| \ |
There was a problem hiding this comment.
🛠️ Refactor suggestion
심볼릭 링크 자기참조(무한 루프) 가능성 및 검증 로직 보강 제안.
npm bin -g가 /usr/local/bin을 반환하는 환경에서 현재 명령은 자기 자신을 가리키는 심볼릭 링크를 만들 수 있어(ELOOP) 빌드가 실패합니다. 대상/목적지가 동일할 경우 링크 생성을 생략하도록 가드하고, 바이너리 검증은 command -v로도 확인하면 좋습니다.
적용 제안:
- && ln -sf "$(npm bin -g)/server-brave-search" /usr/local/bin/server-brave-search \
- \
- # ===== 실행 가능 여부 확인 =====
- && echo "=== npm bin 경로 확인 ===" \
- && npm bin -g \
- && echo "=== server-brave-search 바이너리 확인 ===" \
- && ls -l "$(npm bin -g)/server-brave-search" \
- && ls -l /usr/local/bin/server-brave-search \
- && echo "=== server-brave-search --help 실행 ===" \
- && /usr/local/bin/server-brave-search --help || (echo "[ERROR] server-brave-search 실행 실패" && exit 1) \
+ && BIN_DIR="$(npm bin -g)" \
+ && TARGET="${BIN_DIR}/server-brave-search" \
+ && DEST="/usr/local/bin/server-brave-search" \
+ && if [ "${TARGET}" != "${DEST}" ]; then ln -sf "${TARGET}" "${DEST}"; fi \
+ \
+ # ===== 실행 가능 여부 확인 =====
+ && echo "=== npm bin 경로 확인 ===" \
+ && echo "${BIN_DIR}" \
+ && echo "=== server-brave-search 경로 확인(command -v) ===" \
+ && command -v server-brave-search \
+ && echo "=== server-brave-search 바이너리 확인 ===" \
+ && ls -l "${TARGET}" \
+ && if [ -e "${DEST}" ]; then ls -l "${DEST}"; fi \
+ && echo "=== server-brave-search --help 실행 ===" \
+ && server-brave-search --help \🤖 Prompt for AI Agents
In cs25-service/Dockerfile around lines 31 to 41, the current ln -sf step can
create a self-referential symlink when npm bin -g resolves to /usr/local/bin
causing ELOOP; update the Dockerfile to (1) resolve the absolute paths of the
global npm bin and the intended link target and compare them, skipping ln when
they are identical to avoid creating a self-link, (2) maintain the existing ls
checks but add a command -v server-brave-search check to validate the binary is
on PATH, and (3) ensure the help invocation is still run and that failures emit
a clear error and exit non‑zero.
| @@ -35,8 +48,8 @@ RUN apt-get update \ | |||
| # jar 복사 | |||
| COPY --from=builder /build/cs25-service/build/libs/*.jar app.jar | |||
There was a problem hiding this comment.
💡 Verification agent
🧩 Analysis chain
JAR 복사 글롭 충돌 가능성(다중 매칭 시 실패).
*.jar 글롭은 build/libs에 plain jar와 boot jar가 동시에 존재할 경우 다중 매칭으로 실패할 수 있습니다. 아래 중 하나로 안정화해 주세요.
대안:
- Gradle에서 plain jar 비활성화(권장):
jar { enabled = false }(KTS:tasks.jar { enabled = false }). 이렇게 하면 bootJar만 남아 글롭 충돌이 사라집니다. - bootJar 파일명을 고정:
bootJar { archiveFileName = "app.jar" }후 Docker에서는COPY --from=builder /build/cs25-service/build/libs/app.jar app.jar. - 최소한 패턴을 좁힘:
cs25-service-*-SNAPSHOT.jar등(단, 여전히 단일 매칭을 보장해야 함).
원하시면 Gradle 설정 변경까지 포함한 PR 보조 패치를 제안드릴 수 있습니다.
🏁 Script executed:
#!/bin/bash
# cs25-service 모듈의 Gradle 설정에서 JAR 관련 태스크가 어떻게 정의됐는지 확인
rg -n "tasks\\.jar" -n cs25-service
rg -n "bootJar" -n cs25-service
rg -n "archiveFileName" -n cs25-serviceLength of output: 235
JAR 복사 glob 충돌 위험: 패턴 안정화 필요
현재 cs25-service/Dockerfile(49행)의
COPY --from=builder /build/cs25-service/build/libs/*.jar app.jar
글롭 패턴 *.jar은 build/libs에 plain JAR과 boot JAR이 공존할 경우 다중 매칭으로 실패할 수 있습니다. 또한 cs25-service/build.gradle(55행)에는 bootJar 블록만 정의되어 있고 plain JAR 비활성화나 파일명 고정 설정이 없어 두 개의 JAR이 생성됩니다. 아래 대안 중 하나를 적용해 단일 JAR을 안정적으로 복사하도록 해 주세요:
-
Gradle plain JAR 비활성화 (권장)
cs25-service/build.gradle에 추가tasks.jar { enabled = false }
→ bootJar만 남아 glob 충돌 해소
-
boot JAR 파일명 고정
cs25-service/build.gradle의bootJar블록 수정bootJar { archiveFileName.set("app.jar") }Dockerfile도 함께 변경
- COPY --from=builder /build/cs25-service/build/libs/*.jar app.jar + COPY --from=builder /build/cs25-service/build/libs/app.jar app.jar
-
glob 패턴 구체화
- COPY --from=builder /build/cs25-service/build/libs/*.jar app.jar + COPY --from=builder /build/cs25-service/build/libs/cs25-service-*-SNAPSHOT.jar app.jar
위 방법 중 하나를 선택해 Docker 이미지 빌드가 항상 단일 JAR을 복사하도록 수정해 주세요.
🤖 Prompt for AI Agents
In cs25-service/Dockerfile around line 49 the COPY uses a fragile glob
"build/libs/*.jar" that can match multiple outputs if both plain and boot JARs
are produced; fix by ensuring only one JAR is emitted or by targeting a single
filename: either (A) disable the plain jar in cs25-service/build.gradle by
adding tasks.jar { enabled = false } so only bootJar remains, or (B) set bootJar
archiveFileName to a fixed name like "app.jar" in build.gradle and update the
Dockerfile COPY to reference that exact filename, or (C) tighten the COPY glob
to match the boot JAR filename pattern (for example the boot JAR classifier) so
the Docker step always copies exactly one file.
🔎 작업 내용
/user/local~에러 방지Dockerfile에서 실패시 빌드가 중단되게 변경🛠️ 변경 사항
UserService.createUser()메서드 추가@Email유효성 검증 적용🧩 트러블 슈팅
@Transactional이 적용되지 않음this.→AopProxyUtils.사용)🧯 해결해야 할 문제
UserController에서 비즈니스 로직 일부 처리 → 서비스로 이전 고려 필요📌 참고 사항
🙏 코드 리뷰 전 확인 체크리스트
type :)Summary by CodeRabbit