Feat/sw 72 - CI/CD 자동 배포 시스템 구축 & 인프라 보안 고도화#46
Conversation
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Repository UI Review profile: ASSERTIVE Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 9493756dc3
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
|
||
| on: | ||
| push: | ||
| branches: [ "main", "dev" ] # main, dev 브랜치 직접 푸시 및 타 브랜치 PR 머지(Merge) 완료 시 실행 |
There was a problem hiding this comment.
Limit production deployment to main
This workflow is named and wired as production deployment: it uses PROD_VM1_* secrets and the compose file runs the backend with the prod profile, but it also runs on every push to dev. Because the previous workflow used a dev profile for that branch, retaining dev here means integration-branch pushes can publish and restart production instead of only validating or deploying a dev environment.
Useful? React with 👍 / 👎.
| script: | | ||
| cd ~/Searchweb/deploy | ||
| # 서버에서 최신 이미지로 교체 및 재시작 | ||
| sudo docker compose -f docker-compose-prod-web.yml pull backend |
There was a problem hiding this comment.
Point deploy script at the nested compose file
The SSH script changes to ~/Searchweb/deploy and then runs docker compose -f docker-compose-prod-web.yml, but in this commit the compose file is added under deploy/prod/web/docker-compose-prod-web.yml (repo-wide search finds no root-level deploy/docker-compose-prod-web.yml). On a server that mirrors the repository layout, this deployment fails before pulling or restarting the backend because the compose file path does not exist.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Actionable comments posted: 19
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In @.github/workflows/deploy-prod.yml:
- Line 5: Update the workflow branch filter so production deploys are not
triggered by dev pushes: remove "dev" from the branches array (replace branches:
[ "main", "dev" ] with a main-only filter) and/or add a GitHub Environment
approval gate by specifying environment: production (and configure required
reviewers in the repo settings) in the workflow so deploys require manual
approval; target the change around the branches: [ "main", "dev" ] entry and add
the environment: production stanza to the workflow metadata.
- Line 5: The YAML lint error is caused by extra spaces inside the array
brackets for the branches key; update the branches entry (the "branches" mapping
in the workflow spec) to remove the spaces inside the square brackets so it
reads branches: ["main", "dev"] instead of branches: [ "main", "dev" ].
- Around line 30-36: Replace the unstable "latest" image tag in the "Build and
Push Docker Image" step (docker/build-push-action@v5) with a deterministic tag
using the commit SHA or image digest (for example use tags: ${{
secrets.DOCKERHUB_USERNAME }}/relink-backend:${{ github.sha }}), and update the
other occurrence mentioned (lines 50-51) to use the same deterministic tag so
the workflow always pushes and later deploys the exact same artifact.
- Line 42: The workflow currently references the action with a mutable branch
ref "appleboy/ssh-action@master"; replace this with the action pinned to the
full commit SHA (e.g., appleboy/ssh-action@<commit-sha>) and add the
human-friendly release tag as a trailing comment (e.g., "# v1.2.5") so the run
uses an immutable commit while keeping version context; update the uses entry
that presently reads "appleboy/ssh-action@master" to use the commit SHA for the
desired release.
In `@deploy/dev/docker-compose-dev.yml`:
- Line 16: 현재 dev 컨테이너의 DB 초기화 SQL 마운트는
'../../src/main/resources/db/init_postgres.sql:/docker-entrypoint-initdb.d/01-init.sql:ro'로
되어 있고 prod는 'deploy/prod/db/'에 파일이 있어 경로가 다릅니다; dev와 prod 모두 같은 기준 경로로 통일하도록
docker-compose-dev.yml의 마운트 경로(문자열
'../../src/main/resources/db/init_postgres.sql:/docker-entrypoint-initdb.d/01-init.sql:ro')
또는 prod의 디렉터리 구조를 조정해 하나의 위치로 합치고(예: 모두 src/main/resources/db 또는 모두
deploy/prod/db로 이동), 필요하면 관련 문서/README와 배포 스크립트에도 동일한 경로를 반영하거나 심볼릭 링크를 생성해
유지보수성을 확보하세요.
In `@deploy/prod/db/docker-compose-prod-db.yml`:
- Line 16: The published port mapping "7000:5432" in
deploy/prod/db/docker-compose-prod-db.yml exposes the production DB to the
public network; remove or change this host binding so the container is not
directly reachable (e.g., remove the "7000:5432" port entry or bind it to
localhost only) and enforce access via your VM/network firewall or security
group rules to allow only the VM1 private source; update the compose service
that contains the "7000:5432" ports entry and then verify
firewall/NACL/security-group rules restrict access to the DB host.
In `@deploy/prod/db/init_postgres.sql`:
- Around line 132-144: The schema mixes int and bigint for primary/foreign keys
causing DDL and integrity issues; update the column types so all related PKs and
FKs use bigint consistently — specifically change team.owner_member_id to bigint
to match member.member_id, change team_folder.created_by_member_id to bigint,
change team_folder_permission.member_id to bigint, change team_member.member_id
to bigint, and make team_saved_link.team_saved_link_id and
team_saved_link_tag.team_saved_link_id the same bigint type; also ensure the
corresponding CONSTRAINT fk_* and pk_* definitions are updated or recreated so
the FK references match the new bigint types.
In `@deploy/prod/web/nginx-prod.conf`:
- Around line 2-3: The nginx config currently only listens on port 80 while
docker-compose-prod-web.yml exposes port 443 and mounts /etc/nginx/certs; either
add a proper SSL server block to nginx-prod.conf that listens on 443 and
references the mounted cert files (ssl_certificate and ssl_certificate_key
pointing to /etc/nginx/certs/<your-cert>.crt/.key) to support Cloudflare Full
(strict), or remove the 443 port exposure and the /etc/nginx/certs volume mount
from docker-compose-prod-web.yml if you will not serve TLS from Nginx; update
the chosen file (nginx-prod.conf for adding the server block or
docker-compose-prod-web.yml for removing the port/volume) so the runtime config
and mounts match.
- Line 30: The proxy header currently trusts the client-sent header by
forwarding $http_x_forwarded_proto via proxy_set_header X-Forwarded-Proto;
replace this with using the server-side connection scheme ($scheme) instead so
the upstream receives the actual protocol seen by nginx. Locate proxy_set_header
X-Forwarded-Proto occurrences (references: proxy_set_header,
$http_x_forwarded_proto, $scheme) and change them to forward $scheme rather than
$http_x_forwarded_proto across the config blocks.
In `@docs/Infrastructure/01`. network-and-infra.md:
- Line 7: The Markdown headers are missing a blank line after them causing md022
warnings; for each header (e.g. "## 1. 아키텍처 개요 (High-Level Topology)" and the
other headers referenced around lines 53, 67, 75) insert exactly one blank line
immediately following the header text so there is a single empty line between
the header and the next paragraph or block; ensure you apply this same
one-blank-line rule consistently to all Markdown headers in the file.
- Around line 55-56: Update the production guide text that enforces Cloudflare
"Flexible" SSL: remove the directive that Cloudflare decrypts to VM on HTTP 80
as a required setup and replace it with a clear requirement to use at least
"Full (strict)" between Cloudflare and the origin (including origin
certificates), and add a short note in the same section (referencing the steps
that describe Cloudflare ➡️ VM 1 and VM 1 방화벽 HTTP 80 Ingress) that 80→443
redirects or documented exceptions are allowed only for special cases; ensure
the wording explicitly advises against leaving Cloudflare↔Origin traffic in
plaintext.
In `@docs/Infrastructure/documentation_guide.md`:
- Line 7: The header "### 🎨 1. 시각적 이미지 생성 (generate_image 전용)" is jumping
levels; change it from "###" to "##" so it nests directly under the top-level
"#" and preserves document hierarchy, and verify adjacent section headers use a
consistent sequence (e.g., top-level "#", then "##" for major sections, then
"###" for subsections) to fix the MD001 violation.
- Line 51: The prompt text in the diff incorrectly references
"docs/Infrastructure/architecture.md", which no longer exists; update the quoted
prompt to reference the current infra document name ("01. network-and-infra.md")
and ensure it still instructs to follow the style and color codes from
documentation_guide.md and to minimize text; edit the string in the diff (the
line containing the quoted prompt) so it matches the actual filename and
preserves the styling/minimal-text instruction.
In `@frontend/Dockerfile`:
- Around line 14-18: The runtime stage runs nginx as root and lacks a
HEALTHCHECK: switch the final image to run as a non‑privileged user and add a
container health probe. After the COPY --from=builder /app/out
/usr/share/nginx/html step, ensure the web root is owned by the nonroot nginx
user (chown to the nginx UID/GID used by the image) and add a USER instruction
to drop root privileges; then add a HEALTHCHECK directive that probes the server
(e.g., HTTP GET to / or /healthz using curl/wget) with sensible interval,
timeout and retries so the container runtime can detect unhealthy instances.
Ensure EXPOSE 80 443 remains and that the health endpoint used matches the app
served from /usr/share/nginx/html.
In `@frontend/src/app/page.tsx`:
- Around line 231-261: Remove the large commented JSX blocks (the three
commented sections containing floating background elements: the blurred square
with auto_fix_high icon, the folder_special block, and the rocket block) to
reduce noise in frontend/src/app/page.tsx; if these UI ideas must be preserved,
create an issue or a design doc and reference the mounted prop and the CSS
animation class names (animate-float-soft, animate-float-soft-slow,
animate-float-soft) rather than keeping them commented in the file.
In `@frontend/src/components/layout/Header.tsx`:
- Around line 38-42: Remove the inactive commented notification button block
from the Header component (the JSX block containing the button with classNames
"p-1 text-gray-400..." and the inner span "material-symbols-outlined
!text-[14px]">notifications) so the code is not cluttered; if this feature
should be tracked, create a task/issue instead of keeping the commented JSX and
commit the cleaned Header.tsx.
In `@frontend/src/components/layout/LandingHeader.tsx`:
- Around line 50-52: Replace plain <img> usages in LandingHeader.tsx with
Next.js' Image component: import Image from 'next/image' at the top, then swap
the <img src="/relink_logo.png" ... /> instances (and the other occurrences
around the referenced blocks) to use <Image ... /> and provide required props
(explicit width/height or use layout/fill with parent sizing) and keep className
and alt. Update the NextLink children to render <Image> plus the <span> as
before, ensuring the same styling and hover behavior is preserved.
In `@src/main/java/com/web/SearchWeb/config/common/JpaConfig.java`:
- Around line 21-23: dateTimeProvider() uses OffsetDateTime.now() which relies
on the server's local timezone; update it to produce UTC-based timestamps (e.g.,
OffsetDateTime.now(ZoneOffset.UTC) or use an injectable Clock) so audit
timestamps are consistent across environments; locate the dateTimeProvider
method in JpaConfig and replace the OffsetDateTime.now() call with
OffsetDateTime.now(ZoneOffset.UTC) or refactor to accept a Clock bean and call
OffsetDateTime.now(clock) to allow deterministic testing and configuration.
In `@src/main/java/com/web/SearchWeb/folder/service/MemberFolderServiceImpl.java`:
- Around line 234-236: The recursive deletion in
MemberFolderServiceImpl.deleteRecursive can miss children created concurrently
because it reads the child list once then deletes; to fix, make the delete
atomic by either (A) acquiring a pessimistic lock on the parent row before
enumerating/deleting children (add/use a repository method annotated with
`@Lock`(PESSIMISTIC_WRITE) or a findByIdForUpdate variant and call it inside
deleteRecursive before fetching children), or (B) replace the Java recursion
with a single DB-side recursive delete (use a recursive CTE or a cascade-delete
SQL statement executed via the repository/EntityManager) so the DB performs the
whole subtree deletion atomically. Ensure the change targets
MemberFolderServiceImpl.deleteRecursive and the FolderRepository/DAO methods
used to fetch/delete children.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: f21ecc2e-95fc-4bef-9ddc-6ee587bb9869
⛔ Files ignored due to path filters (6)
docs/Infrastructure/images/relink_architecture.pngis excluded by!**/*.pngfrontend/pnpm-lock.yamlis excluded by!**/pnpm-lock.yamlfrontend/public/relink_logo.pngis excluded by!**/*.pngfrontend/public/relink_logo_black.pngis excluded by!**/*.pngfrontend/public/relink_logo_white.pngis excluded by!**/*.pngfrontend/src/app/favicon.icois excluded by!**/*.ico
📒 Files selected for processing (36)
.github/workflows/deploy-prod.yml.github/workflows/deploy-test.yml.gitignoredeploy/dev/docker-compose-dev.ymldeploy/dev/nginx-dev.confdeploy/local/docker-compose-local.ymldeploy/prod/db/docker-compose-prod-db.ymldeploy/prod/db/init_postgres.sqldeploy/prod/web/docker-compose-prod-web.ymldeploy/prod/web/nginx-prod.confdocker-compose.ymldocs/Infrastructure/01. network-and-infra.mddocs/Infrastructure/documentation_guide.mdfrontend/.dockerignorefrontend/Dockerfilefrontend/README.mdfrontend/next.config.tsfrontend/package.jsonfrontend/src/app/globals.cssfrontend/src/app/layout.tsxfrontend/src/app/login/page.tsxfrontend/src/app/page.tsxfrontend/src/components/layout/Header.tsxfrontend/src/components/layout/LandingHeader.tsxfrontend/src/components/layout/Sidebar.tsxfrontend/src/lib/config/backend.tssrc/main/java/com/web/SearchWeb/bookmark/dao/BookmarkDao.javasrc/main/java/com/web/SearchWeb/bookmark/dao/MybatisBookmarkDao.javasrc/main/java/com/web/SearchWeb/config/common/JpaConfig.javasrc/main/java/com/web/SearchWeb/folder/domain/MemberFolder.javasrc/main/java/com/web/SearchWeb/folder/service/MemberFolderServiceImpl.javasrc/main/resources/application-dev.propertiessrc/main/resources/application-local.propertiessrc/main/resources/application-prod.propertiessrc/main/resources/application.propertiessrc/main/resources/mapper/bookmark-mapper.xml
💤 Files with no reviewable changes (2)
- .github/workflows/deploy-test.yml
- docker-compose.yml
|
|
||
| on: | ||
| push: | ||
| branches: [ "main", "dev" ] # main, dev 브랜치 직접 푸시 및 타 브랜치 PR 머지(Merge) 완료 시 실행 |
There was a problem hiding this comment.
운영 배포를 dev push에 직접 연결하지 마세요.
Line 5 설정은 dev 브랜치 push만으로도 프로덕션 배포가 실행됩니다. 최소한 main 전용으로 제한하거나 GitHub Environment 승인 게이트를 추가해 운영 반영 경로를 분리하세요.
🧰 Tools
🪛 YAMLlint (1.38.0)
[error] 5-5: too many spaces inside brackets
(brackets)
[error] 5-5: too many spaces inside brackets
(brackets)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/deploy-prod.yml at line 5, Update the workflow branch
filter so production deploys are not triggered by dev pushes: remove "dev" from
the branches array (replace branches: [ "main", "dev" ] with a main-only filter)
and/or add a GitHub Environment approval gate by specifying environment:
production (and configure required reviewers in the repo settings) in the
workflow so deploys require manual approval; target the change around the
branches: [ "main", "dev" ] entry and add the environment: production stanza to
the workflow metadata.
YAMLlint 브래킷 공백 오류를 수정하세요.
Line 5는 branches: [ "main", "dev" ]로 되어 있어 lint 에러가 발생합니다. branches: ["main", "dev"]로 정리하세요.
🧰 Tools
🪛 YAMLlint (1.38.0)
[error] 5-5: too many spaces inside brackets
(brackets)
[error] 5-5: too many spaces inside brackets
(brackets)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/deploy-prod.yml at line 5, The YAML lint error is caused
by extra spaces inside the array brackets for the branches key; update the
branches entry (the "branches" mapping in the workflow spec) to remove the
spaces inside the square brackets so it reads branches: ["main", "dev"] instead
of branches: [ "main", "dev" ].
| - name: Build and Push Docker Image | ||
| uses: docker/build-push-action@v5 | ||
| with: | ||
| context: . | ||
| push: true | ||
| tags: ${{ secrets.DOCKERHUB_USERNAME }}/relink-backend:latest # 도커 이미지 만들어서 업로드 | ||
|
|
There was a problem hiding this comment.
latest 태그 기반 배포는 빌드-배포 일관성을 깨뜨립니다.
Line 35/50의 latest는 동시 푸시 시 다른 커밋 이미지가 배포될 수 있습니다. ${{ github.sha }} 태그(또는 digest)로 고정해 동일 아티팩트를 배포하세요.
변경 예시
- tags: ${{ secrets.DOCKERHUB_USERNAME }}/relink-backend:latest
+ tags: |
+ ${{ secrets.DOCKERHUB_USERNAME }}/relink-backend:${{ github.sha }}
+ ${{ secrets.DOCKERHUB_USERNAME }}/relink-backend:latest
...
- sudo docker compose -f docker-compose-prod-web.yml pull backend
- sudo docker compose -f docker-compose-prod-web.yml up -d backend
+ export IMAGE_TAG=${{ github.sha }}
+ sudo -E docker compose -f docker-compose-prod-web.yml pull backend
+ sudo -E docker compose -f docker-compose-prod-web.yml up -d backendAlso applies to: 50-51
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/deploy-prod.yml around lines 30 - 36, Replace the unstable
"latest" image tag in the "Build and Push Docker Image" step
(docker/build-push-action@v5) with a deterministic tag using the commit SHA or
image digest (for example use tags: ${{ secrets.DOCKERHUB_USERNAME
}}/relink-backend:${{ github.sha }}), and update the other occurrence mentioned
(lines 50-51) to use the same deterministic tag so the workflow always pushes
and later deploys the exact same artifact.
| runs-on: ubuntu-latest | ||
| steps: | ||
| - name: Deploy to Oracle Cloud VM1 | ||
| uses: appleboy/ssh-action@master # 서버에 원격 접속 |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
What is the recommended secure pinning strategy for GitHub Actions (tag vs commit SHA), and what is the latest stable release for appleboy/ssh-action?
💡 Result:
The recommended secure pinning strategy for GitHub Actions is to use the full commit SHA rather than version tags [1][2][3]. Git tags are mutable, meaning they can be deleted, moved, or force-pushed to point to different commits [1][4][5]. This vulnerability has been exploited in several supply chain attacks where attackers rewrote tags to inject malicious code into workflows [4][5][6]. Pinning to a full commit SHA ensures immutability, guaranteeing that the exact code you have tested and validated is executed every time [1][2][3]. For best practices, it is recommended to use the following format in your workflow files, which combines the security of a SHA with the readability of a tag: uses: owner/action@commit-sha # v1.2.5 This approach allows you to maintain clarity regarding which version the SHA corresponds to while protecting your pipeline from unexpected changes [2]. The latest stable release for appleboy/ssh-action as of May 17, 2026, is v1.2.5 [7][8].
Citations:
- 1: https://www.romainlespinasse.dev/posts/github-actions-commit-sha-pinning/
- 2: https://dev.to/ciberconscientes/github-actions-why-you-should-pin-your-actions-to-a-specific-version-1513
- 3: https://www.stepsecurity.io/blog/pinning-github-actions-for-enhanced-security-a-complete-guide
- 4: Pin GitHub Actions to commit SHAs instead of version tags ankitects/anki#4722
- 5: https://dev.to/kanywst/hacking-github-from-tag-rewrites-to-dangling-commits-where-the-git-protocol-trusts-you-without-2o4h
- 6: https://dev.to/nkuziev-sec/why-fixed-container-image-versions-matter-lessons-from-the-trivy-supply-chain-attack-29ke
- 7: https://github.com/appleboy/ssh-action/releases/tag/v1.2.5
- 8: https://github.com/appleboy/ssh-action
@master 브랜치 참조를 커밋 SHA로 고정하세요.
Line 42의 appleboy/ssh-action@master는 중요한 공급망 위험입니다. 브랜치 참조는 변경 가능하므로 공격자가 태그를 재작성하거나 강제 푸시할 수 있습니다. 대신 전체 커밋 SHA로 고정하되, 버전 정보를 주석으로 포함하세요.
권장 형식: uses: appleboy/ssh-action@<commit-sha> # v1.2.5
현재 최신 안정 버전은 v1.2.5입니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In @.github/workflows/deploy-prod.yml at line 42, The workflow currently
references the action with a mutable branch ref "appleboy/ssh-action@master";
replace this with the action pinned to the full commit SHA (e.g.,
appleboy/ssh-action@<commit-sha>) and add the human-friendly release tag as a
trailing comment (e.g., "# v1.2.5") so the run uses an immutable commit while
keeping version context; update the uses entry that presently reads
"appleboy/ssh-action@master" to use the commit SHA for the desired release.
| - "7000:5432" | ||
| volumes: | ||
| - postgres-data:/var/lib/postgresql/data | ||
| - ../../src/main/resources/db/init_postgres.sql:/docker-entrypoint-initdb.d/01-init.sql:ro |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "[1] init_postgres.sql 후보 파일 검색"
fd -i "init_postgres.sql"
echo
echo "[2] dev compose가 참조하는 경로 존재 확인"
test -f src/main/resources/db/init_postgres.sql && echo "OK: src/main/resources/db/init_postgres.sql 존재" || echo "MISSING: src/main/resources/db/init_postgres.sql"
echo
echo "[3] prod DB compose 참조 파일 확인"
test -f deploy/prod/db/init_postgres.sql && echo "OK: deploy/prod/db/init_postgres.sql 존재" || echo "MISSING: deploy/prod/db/init_postgres.sql"Repository: Searchweb-Dev/Searchweb-Back
Length of output: 325
DB 초기화 SQL 마운트 경로는 존재하므로 현재 컨테이너 기동 시 문제없습니다.
다만 dev와 prod 환경의 초기화 SQL 파일 위치가 다릅니다 (dev: src/main/resources/db/, prod: deploy/prod/db/). 향후 경로 기준을 통일하면 유지보수가 더 용이할 것입니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@deploy/dev/docker-compose-dev.yml` at line 16, 현재 dev 컨테이너의 DB 초기화 SQL 마운트는
'../../src/main/resources/db/init_postgres.sql:/docker-entrypoint-initdb.d/01-init.sql:ro'로
되어 있고 prod는 'deploy/prod/db/'에 파일이 있어 경로가 다릅니다; dev와 prod 모두 같은 기준 경로로 통일하도록
docker-compose-dev.yml의 마운트 경로(문자열
'../../src/main/resources/db/init_postgres.sql:/docker-entrypoint-initdb.d/01-init.sql:ro')
또는 prod의 디렉터리 구조를 조정해 하나의 위치로 합치고(예: 모두 src/main/resources/db 또는 모두
deploy/prod/db로 이동), 필요하면 관련 문서/README와 배포 스크립트에도 동일한 경로를 반영하거나 심볼릭 링크를 생성해
유지보수성을 확보하세요.
| env_file: | ||
| - ./.env | ||
| ports: | ||
| - "7000:5432" |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
DB 포트 노출은 네트워크 레벨에서 반드시 제한하세요.
Line 16의 7000:5432는 운영 DB 공격면을 넓힙니다. 보안그룹/NACL/호스트 방화벽에서 VM1의 private source만 허용하도록 제한하는 것을 권장합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@deploy/prod/db/docker-compose-prod-db.yml` at line 16, The published port
mapping "7000:5432" in deploy/prod/db/docker-compose-prod-db.yml exposes the
production DB to the public network; remove or change this host binding so the
container is not directly reachable (e.g., remove the "7000:5432" port entry or
bind it to localhost only) and enforce access via your VM/network firewall or
security group rules to allow only the VM1 private source; update the compose
service that contains the "7000:5432" ports entry and then verify
firewall/NACL/security-group rules restrict access to the DB host.
| {/* 마지막 남은 배경 요소들 주석 처리 | ||
| <div className="absolute top-10 right-[10%] h-4 w-4 rounded-full bg-blue-400/20 blur-sm animate-pulse"></div> | ||
| {/* Floating AI Magic Icon - Enhanced Background Element (Bottom Left) - Optimized */} | ||
|
|
||
| <div className={`absolute bottom-[12%] left-[4%] lg:left-[10%] hidden sm:block z-0 transition-opacity duration-1000 pointer-events-none filter blur-[2px] ${mounted ? 'opacity-70 dark:opacity-50 animate-float-soft' : 'opacity-0'}`} style={{ animationDelay: '1s' }}> | ||
| <div className="p-2.5 rounded-3xl bg-white/40 dark:bg-slate-800/40 border border-white/40 dark:border-white/10 rotate-[15deg] scale-105 shadow-lg"> | ||
| <div className="flex h-12 w-12 items-center justify-center rounded-2xl bg-violet-400/10 text-violet-500/80"> | ||
| <span className="material-symbols-outlined text-[28px]">auto_fix_high</span> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| */} | ||
|
|
||
| {/* Floating Folder Icon - Enhanced Background Element (Adjusted for overlap) - Optimized */} | ||
| {/* Floating Folder Icon 주석 처리 | ||
| <div className={`absolute top-[18%] left-[1%] lg:left-[5%] hidden sm:block z-0 transition-opacity duration-1000 pointer-events-none filter blur-[2px] ${mounted ? 'opacity-60 dark:opacity-40 animate-float-soft-slow' : 'opacity-0'}`}> | ||
| <div className="p-2.5 rounded-3xl bg-white/40 dark:bg-slate-800/40 border border-white/40 dark:border-white/10 rotate-[-15deg] scale-110 shadow-xl"> | ||
| <div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-amber-400/10 text-amber-500/80"> | ||
| <span className="material-symbols-outlined text-[32px]">folder_special</span> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| */} | ||
|
|
||
| {/* Floating Rocket Icon - Enhanced Background Element - Optimized */} | ||
| {/* Floating Rocket Icon 주석 처리 | ||
| <div className={`absolute bottom-[14%] right-[0%] lg:right-[4%] hidden sm:block z-0 transition-opacity duration-1000 pointer-events-none filter blur-[1.5px] ${mounted ? 'opacity-70 dark:opacity-50 animate-float-soft' : 'opacity-0'}`} style={{ animationDelay: '2s' }}> | ||
| <div className="p-2.5 rounded-3xl bg-white/50 dark:bg-slate-700/50 border border-white/50 dark:border-blue-400/20 rotate-[12deg] scale-85 shadow-lg"> | ||
| <div className="flex h-14 w-14 items-center justify-center rounded-2xl bg-linear-to-br from-blue-500/20 to-purple-600/20 dark:from-blue-500/30 dark:to-purple-600/30 text-blue-600 dark:text-purple-400"> | ||
| <span className="material-symbols-outlined text-[32px]">rocket</span> | ||
| </div> | ||
| </div> | ||
| </div> | ||
| */} |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
주석 처리된 대규모 JSX 블록은 제거해 주세요.
현재 형태는 유지보수 시 노이즈가 크고 실제 코드 경로 파악을 어렵게 합니다. 필요하면 이슈/문서로 남기고 코드에서는 삭제하는 편이 좋습니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/app/page.tsx` around lines 231 - 261, Remove the large commented
JSX blocks (the three commented sections containing floating background
elements: the blurred square with auto_fix_high icon, the folder_special block,
and the rocket block) to reduce noise in frontend/src/app/page.tsx; if these UI
ideas must be preserved, create an issue or a design doc and reference the
mounted prop and the CSS animation class names (animate-float-soft,
animate-float-soft-slow, animate-float-soft) rather than keeping them commented
in the file.
| {/* 향후 기능 개발 예정이라 주석 | ||
| <button type="button" className="p-1 text-gray-400 dark:text-gray-500 hover:bg-gray-100 dark:hover:bg-white/5 rounded-md transition-colors"> | ||
| <span className="material-symbols-outlined !text-[14px]">notifications</span> | ||
| </button> | ||
| */} |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
비활성 기능은 주석 대신 코드에서 제거해 주세요.
현재 주석 블록은 코드 가독성만 떨어뜨립니다. 필요 시 작업 이슈로 추적하고, 재도입 시점에 다시 추가하는 것이 더 안전합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/layout/Header.tsx` around lines 38 - 42, Remove the
inactive commented notification button block from the Header component (the JSX
block containing the button with classNames "p-1 text-gray-400..." and the inner
span "material-symbols-outlined !text-[14px]">notifications) so the code is not
cluttered; if this feature should be tracked, create a task/issue instead of
keeping the commented JSX and commit the cleaned Header.tsx.
| <img src="/relink_logo.png" alt="ReLink" className="w-6 h-6 object-contain group-hover:scale-110 transition-transform" /> | ||
| <span className="text-xl font-bold tracking-tight text-white uppercase sm:normal-case">ReLink</span> | ||
| </NextLink> |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
로고 렌더링은 next/image로 통일하는 것이 좋습니다.
헤더 핵심 자산을 <img>로 두면 Next.js의 이미지 최적화/레이아웃 안정성 이점을 놓칩니다. 동일 자산을 반복 렌더링하는 구간이라 Image 컴포넌트로 맞추는 편이 안전합니다.
변경 예시
+ import Image from "next/image";
...
- <img src="/relink_logo.png" alt="ReLink" className="w-6 h-6 object-contain group-hover:scale-110 transition-transform" />
+ <Image
+ src="/relink_logo.png"
+ alt="ReLink"
+ width={24}
+ height={24}
+ className="w-6 h-6 object-contain group-hover:scale-110 transition-transform"
+ priority
+ />Also applies to: 61-63, 135-137
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@frontend/src/components/layout/LandingHeader.tsx` around lines 50 - 52,
Replace plain <img> usages in LandingHeader.tsx with Next.js' Image component:
import Image from 'next/image' at the top, then swap the <img
src="/relink_logo.png" ... /> instances (and the other occurrences around the
referenced blocks) to use <Image ... /> and provide required props (explicit
width/height or use layout/fill with parent sizing) and keep className and alt.
Update the NextLink children to render <Image> plus the <span> as before,
ensuring the same styling and hover behavior is preserved.
| public DateTimeProvider dateTimeProvider() { | ||
| // Spring Data JPA Auditing이 OffsetDateTime 형식을 지원하도록 현재 시간을 반환함 | ||
| return () -> Optional.of(OffsetDateTime.now()); |
There was a problem hiding this comment.
감사 시각의 타임존을 고정하세요(UTC 권장).
Line 23의 OffsetDateTime.now()는 서버 로컬 타임존에 의존해서 환경별 감사 시각이 달라질 수 있습니다. 감사 데이터 정렬/비교 일관성을 위해 OffsetDateTime.now(ZoneOffset.UTC) 또는 주입 가능한 Clock 기반으로 고정하는 편이 안전합니다.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/web/SearchWeb/config/common/JpaConfig.java` around lines 21
- 23, dateTimeProvider() uses OffsetDateTime.now() which relies on the server's
local timezone; update it to produce UTC-based timestamps (e.g.,
OffsetDateTime.now(ZoneOffset.UTC) or use an injectable Clock) so audit
timestamps are consistent across environments; locate the dateTimeProvider
method in JpaConfig and replace the OffsetDateTime.now() call with
OffsetDateTime.now(ZoneOffset.UTC) or refactor to accept a Clock bean and call
OffsetDateTime.now(clock) to allow deterministic testing and configuration.
| // 재귀적으로 폴더 및 북마크 삭제 수행 | ||
| deleteRecursive(memberId, folder); | ||
| } |
There was a problem hiding this comment.
재귀 삭제가 동시 생성과 레이스가 나면 하위 폴더가 살아남을 수 있습니다.
Line 245-250에서 자식 목록을 한 번 조회한 뒤 순회 삭제합니다. 같은 시점에 다른 요청이 해당 폴더 아래 자식을 생성하면 이번 순회에서 누락되어, 부모는 삭제됐는데 자식은 활성 상태로 남을 수 있습니다. 삭제/생성 경로에 부모 행 잠금(FOR UPDATE/PESSIMISTIC_WRITE)을 적용하거나, DB 단일 재귀 삭제 쿼리로 원자적으로 처리해 주세요.
Also applies to: 241-250
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@src/main/java/com/web/SearchWeb/folder/service/MemberFolderServiceImpl.java`
around lines 234 - 236, The recursive deletion in
MemberFolderServiceImpl.deleteRecursive can miss children created concurrently
because it reads the child list once then deletes; to fix, make the delete
atomic by either (A) acquiring a pessimistic lock on the parent row before
enumerating/deleting children (add/use a repository method annotated with
`@Lock`(PESSIMISTIC_WRITE) or a findByIdForUpdate variant and call it inside
deleteRecursive before fetching children), or (B) replace the Java recursion
with a single DB-side recursive delete (use a recursive CTE or a cascade-delete
SQL statement executed via the repository/EntityManager) so the DB performs the
whole subtree deletion atomically. Ensure the change targets
MemberFolderServiceImpl.deleteRecursive and the FolderRepository/DAO methods
used to fetch/delete children.
b2a87d1 to
4ecefb1
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 4ecefb1dee
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| on: | ||
| push: | ||
| branches: [ "main", "dev" ] # main, dev 브랜치 직접 푸시 및 타 브랜치 PR 머지(Merge) 완료 시 실행 | ||
| pull_request_target: # PR 시에도 Secrets가 주입되도록 pull_request_target 사용 (임시 테스트용) |
There was a problem hiding this comment.
Remove production deployment from PR-target events
When a PR targets main or dev, this pull_request_target trigger runs the same jobs that log in to Docker Hub and SSH into PROD_VM1 to deploy. Because actions/checkout on pull_request_target checks out the base branch by default, this does not validate the PR changes, but it still republishes latest and restarts production whenever a PR is opened or updated against these branches.
Useful? React with 👍 / 👎.
|
|
||
| # (2) Nginx 웹 서버 | ||
| nginx: | ||
| image: ${DOCKERHUB_USERNAME}/relink-frontend:latest |
There was a problem hiding this comment.
Publish the frontend image before composing nginx
This production compose file requires ${DOCKERHUB_USERNAME}/relink-frontend:latest for nginx, but a repo-wide search of this commit finds only the backend image being built and pushed in .github/workflows/deploy-prod.yml (relink-backend:latest) and no publisher for this frontend tag. On a clean host, or whenever nginx is recreated, compose will pull an image this repo never publishes, leaving the frontend missing/stale or the nginx container unable to start.
Useful? React with 👍 / 👎.
bb6943b to
ecb4564
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: ecb4564bde
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| cd ~/Searchweb/deploy | ||
| # 서버에서 최신 이미지로 교체 및 재시작 | ||
| sudo docker compose -f docker-compose-prod-web.yml pull backend | ||
| sudo docker compose -f docker-compose-prod-web.yml up -d backend |
There was a problem hiding this comment.
Start nginx during production deploy
In this deploy-prod.yml workflow, the SCP step copies both docker-compose-prod-web.yml and nginx-prod.conf, and the compose file defines an nginx service, but the deploy command only runs docker compose ... up -d backend. Docker documents the usage as docker compose up [OPTIONS] [SERVICE...] and says it starts containers for a service (https://docs.docker.com/reference/cli/docker/compose/up/), so on a clean VM, or after nginx config/image changes, the nginx container is never created or recreated and the copied config is not applied.
Useful? React with 👍 / 👎.
ecb4564 to
579260b
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 579260b63b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| on: | ||
| push: | ||
| branches: [ "main", "dev" ] # main, dev 브랜치 직접 푸시 및 타 브랜치 PR 머지(Merge) 완료 시 실행 | ||
| pull_request: # PR 단계에서 수정한 워크플로우 파일이 즉각 작동하도록 원복 (동일 저장소 내 PR이므로 Secrets 주입 가능) |
There was a problem hiding this comment.
Remove production deploys from PR events
With the current pull_request trigger, every same-repository PR opened or updated against main or dev runs the same unguarded jobs that push relink-backend:latest and SSH into PROD_VM1. Fresh evidence in the current diff is that this is now a pull_request trigger (not just the previously discussed pull_request_target case), so normal PR validation can still publish and restart production before the PR is merged.
Useful? React with 👍 / 👎.
579260b to
a249658
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: fcfa7d2b65
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| services: | ||
| # (1) 백엔드 스프링 부트 앱 | ||
| backend: | ||
| image: ${DOCKERHUB_USERNAME}/relink-backend:latest |
There was a problem hiding this comment.
Export the Docker Hub namespace before composing
When this deploy runs on a VM where DOCKERHUB_USERNAME is not already exported or present in ~/Searchweb/deploy/.env, the remote docker compose ... pull/up resolves this image to /relink-backend:latest and fails before starting the backend. I checked .github/workflows/deploy-prod.yml and the SSH script only cds and runs Compose; Docker documents that Compose interpolation comes from the shell or a .env file in the working directory, and that unset variables are substituted with an empty string (https://docs.docker.com/compose/how-tos/environment-variables/variable-interpolation/).
Useful? React with 👍 / 👎.
cd1bd81 to
1598d32
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 1598d32915
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @@ -1,3 +1,8 @@ | |||
| FROM eclipse-temurin:17-jdk-alpine AS builder | |||
| WORKDIR /app | |||
| COPY . . | |||
There was a problem hiding this comment.
Exclude local env files from backend Docker build
When this image is built from a developer or deployment checkout that contains the gitignored .env, this broad COPY . . includes it because the new root .dockerignore does not exclude .env or .env.* (unlike the frontend one). That puts secrets into the build context and builder-layer history, which is especially risky with remote builders or exported caches; add the env patterns to .dockerignore or copy only the Gradle/project files needed for the jar.
Useful? React with 👍 / 👎.
- `LinkAnalysisContextLevel`: 컨텍스트 상태 정의: 사용자의 기존 분류 체계 성숙도(커스텀 폴더 개수 기준)에 따라 LOW_CONTEXT와 HIGH_CONTEXT 상태를 구분하기 위한 열거형(Enum) 타입 추가. - `LinkAnalysisServiceImpl`: 컨텍스트 판정 및 프롬프트 동적 주입 구현: 사용자별 폴더 구조 기반으로 컨텍스트 레벨을 계산한 뒤, 데이터 부족 시 신규 폴더 제안을, 데이터 충분 시 기존 폴더 재사용을 유도하는 맞춤형 추천 정책(modeInstruction)을 프롬프트 템플릿에 동적으로 전달하도록 아키텍처 개선. - `LinkAnalysisServiceImpl`: 미분류 제안 보정 로직 강화: LOW_CONTEXT 상태에서 AI가 UNORGANIZED(미분류) 폴더를 제안할 경우, 링크 내용 기반으로 추출된 첫 번째 구체적 태그를 사용하여 신규 폴더명으로 대체하도록 후처리 정교화. - `FolderContext`: 추천 기반 메타데이터 구조 최적화: 북마크 개수 등 폴더 컨텍스트 정보가 추천 우선순위를 왜곡하지 않고 순수하게 폴더의 맥락 근거량으로만 활용될 수 있도록 데이터 전달 구조 정비. - `link-analysis-system-v4.md`: 모드별 시스템 프롬프트 지시문 보강: 사용자 컨텍스트 상태(LOW/HIGH)에 따른 행동 강령을 분리 명시하여, 약한 폴더명 매칭이나 부자연스러운 기존 분류 강제 할당 현상 방지 및 추천 품질 최적화.
1598d32 to
bf5e189
Compare
- JpaConfig.java: 감사 시각 일관성 확보: 시스템 로컬 타임존 의존성을 제거하고 테스트 시 시간 모킹(Mocking)을 용이하게 하도록 UTC 기준의 Clock 빈을 등록하고, DateTimeProvider가 이를 주입받아 OffsetDateTime을 UTC 기준으로 생성하도록 개선. - MemberFolderJpaDao.java: 동시성 정합성 확보: 다중 스레드 환경에서 동일 폴더에 생성 및 삭제 요청이 몰릴 때 발생할 수 있는 레이스 컨디션을 방지하기 위해 특정 폴더 조회(findById) 및 하위 폴더 전체 조회 시 비관적 쓰기 락(PESSIMISTIC_WRITE)을 적용하는 쿼리 정의. - MemberFolderServiceImpl.java: 폴더 작업 안정성 향상: 폴더 생성 시 부모 폴더를 조회할 때 비관적 락이 적용된 findByIdForUpdate를 호출하여 동시 검증 정합성을 제고하고, 삭제 시에도 비관적 락을 걸고 하위 폴더를 재귀 조회하여 삭제 진행 중에 새로운 하위 폴더가 누락 없이 안전하게 동시 삭제되도록 동시성 제어 적용.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 10225b952b
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| env_file: | ||
| - ./.env | ||
| ports: | ||
| - "${PROD_VM1_INTERNAL_IP}:7000:5432" |
There was a problem hiding this comment.
Bind PostgreSQL to the DB host interface
Use the DB host’s own interface (or 0.0.0.0) for the published port instead of ${PROD_VM1_INTERNAL_IP}. In this compose file, postgres runs on VM2 but the host binding uses the VM1 variable, while the web compose already treats VM2 separately via ${PROD_VM2_INTERNAL_IP}; with normal env assignments this causes Docker to fail port publication because VM2 does not own VM1’s IP, so the production DB container cannot start.
Useful? React with 👍 / 👎.
- Dockerfile: 컨테이너 실행 보안성 확보: 루트(root) 계정 실행으로 인한 취약점 노출 및 컨테이너 탈옥(Container Escape) 위협을 미연에 방지하기 위해 베이스 이미지를 nginxinc/nginx-unprivileged:alpine 일반 계정 권한 이미지로 전면 전환하고, 내부 수신 포트를 8080 일반 계정 권한 포트로 개방하며 wget을 활용한 30초 주기 헬스체크 메커니즘 구축.
- nginx-prod.conf: 서비스 트래픽 신뢰성 확보: 일반 계정 권한 웹 서버 컨테이너 전향에 맞추어 수신 포트를 8080으로 수정하고, Cloudflare 프록시 환경에서 클라이언트의 실제 접속 IP가 오리진 서버에서 유실되는 병목 장애를 해결하기 위해 Cloudflare 공식 IP 대역 리스트 전체에 대한 set_real_ip_from 설정 및 real_ip_header CF-Connecting-IP 헤더 복원 추가. 아울러 Docker 헬스체크용 User-Agent Wget 요청 시 자원 소모를 방지하기 위한 access_log off 예외 처리 반영.
- docker-compose-prod-web.yml: 무중단 빌드 정합성 확보: 도커 이미지 pull 및 up 명령 수행 시 latest 태그 혼용에 따른 정합성 파괴 문제를 원천 차단하기 위해 github.sha 고유 태그(${IMAGE_TAG}) 변수를 백엔드 및 Nginx 이미지에 매핑하고, Nginx의 수신 포트를 호스트 80에서 컨테이너 내부 8080 일반 계정 권한 포트로 전달하도록 포트 바인딩 조정.
- docker-compose-prod-db.yml: 데이터베이스 인프라 격리 강화: 데이터베이스 포트(7000)가 공인 IP 전체에 무방비하게 개방되던 중대 보안 결함을 해결하기 위해 호스트 포트 바인딩 규격에 PROD_VM1_INTERNAL_IP 주소를 명시하여 외부 네트워크로부터 데이터베이스 서버 노출 차단.
- deploy-prod.yml: 배포 자동화 파이프라인 안전성 향상: 운영 배포 시 latest 태그에 의한 이미지 혼동을 막기 위해 이미지 pull 및 up 실행 구문에 $((github.sha))를 환경 변수 IMAGE_TAG로 강제 주입하도록 쉘 구성 고도화. 또한 보안 강화를 위해 기존 master 브랜치 기반 액션(scp, ssh)을 신뢰할 수 있는 특정 릴리즈 SHA 해시 버전으로 고정.
- .dockerignore: 민감 데이터 유출 차단: 도커 이미지 빌드 컨텍스트 전송 과정에서 외부로 유출되어서는 안 되는 환경 변수 설정 파일(.env, .env.*)이 이미지 레이어 내부로 혼입되지 않도록 원천 격리 처리.
- globals.css: 사용자 경험 완성도 제고: UI 마우스 드래그 혹은 더블 클릭 동작 시 텍스트 영역이 원치 않게 반전 선택되거나 버튼 클릭 시 텍스트 커서가 깜빡이는 인터페이스 노이즈를 제거하기 위해 body와 button에 select-none 스타일을 주입하고, input, textarea 등 실질 편집 컨트롤러에 한하여 select-text 및 cursor-text를 명시적으로 환원하여 상호작용성 최적화. - layout.tsx: 웹 폰트 렌더링 성능 최적화: 외부 리소스 로딩 지연 시 발생하는 레이아웃 흔들림(Layout Shift) 및 폰트 미인식 화면 깜빡임(FOIT) 현상을 최소화하기 위해 Google Fonts 도메인 연동을 위한 preconnect 설정을 정의하고 Material Symbols Outlined 스타일시트를 preload 및 display=block 지시어로 최적화하여 조기 서빙 구현. - LandingHeader.tsx: 랜딩 페이지 로딩 성능 개선: 랜딩 로고 이미지를 일반 HTML img 엘리먼트에서 Next.js 프레임워크 전용 Image 컴포넌트로 개편하여 반응형 사이즈 자동 변환 및 정적 레이아웃 누적 이동 방지 효과 획득. - Header.tsx: 헤더 상호작용 피드백 보강: 상단 내비게이션 바 로고 및 타이틀 텍스트 영역에 select-none 스타일을 보완하여 잦은 클릭 과정에서 영역 반전이 일어나는 시각적 불편 요소 정돈. - Sidebar.tsx: 사이드바 데이터 효율성 및 스타일 단일화: 현재 사용하지 않는 Pinned 폴더 관련 무거운 API 호출 로직과 주석 구문들을 주석 처리하여 컴포넌트 렌더링 속도 향상을 꾀하고, 구글 아이콘 클래스 속성의 느낌표(!) 위치를 Tailwind 표준 규격인 !text-[16px] 형태로 리팩토링하여 디자인 무결성 확보. - FolderCard.tsx: 폴더 카드 선택 피드백 고급화: 사용자가 폴더 선택 여부를 한눈에 식별할 수 있도록 기본 카드 보더 변경 방식에서 탈피하여 border-purple-500 bg-purple-50/40 dark:border-purple-400 dark:bg-purple-500/10 shadow-sm 구조의 고급스러운 시각 효과를 투영하여 디자인 정밀도 강화. - my-links/page.tsx: 핀 폴더 배지 감성 연동: 사용자가 핀 폴더 클릭 시 주변 요소가 찌그러지거나 레이아웃 링이 어색하게 깨지는 불일치를 해결하고자 ring-inset ring-white/60 및 shadow-md scale-[1.02] 모던 스타일 규칙을 추가하여 일관된 깊이감 제공. - bookmarkApi.ts: 태그 리스트 데이터 정합성 유지: 북마크를 새로 생성하거나 정보를 업데이트, 혹은 삭제를 진행했을 때 화면 우측 상세 창의 태그 목록 캐시가 만료되지 않아 오래된 정보(Stale state)가 표기되는 현상을 치유하기 위해 북마크 변경 API 성공 시 tags 쿼리 키를 강제 갱신(invalidateQueries)하도록 콜백 연결.
- favicon.ts: 지능형 파비콘 헬퍼 유틸리티 신설: 입력받은 무작위 웹 주소로부터 정규식을 이용하여 서브도메인을 안전하게 거르고 co.kr 등 국가 도메인을 감안하여 정확한 루트 호스트네임을 연산해내는 유틸리티 함수들을 구현하고, 구글 API 연동형 및 /favicon.ico 직접 요청용 파비콘 경로 함수를 정적 라이브러리로 수립. - SaveLinkDialog.tsx: 파비콘 로드 성공률 획기적 제고 및 로더 교체: 링크 등록 창에서 구글 파비콘 API가 기본 지구본 이미지(16x16)를 반환하거나 에러를 내뿜을 경우 즉시 previewFaviconUrl을 basedomain 조회에서 direct /favicon.ico 경로 조회로 3단계에 걸쳐 자동 폴백하는 제어 로직을 삽입하고, 기존 텍스트 회전 방식 대신 프리미엄 SVG Spinner 컴포넌트를 연계하여 인터페이스 세련미 연출. - RightPanel.tsx: 상세 상세 패널 파비콘 연동 및 드롭다운 고도화: 저장된 북마크 개별 카드에 다단계 파비콘 폴백 이미지 로더(BaseDomain -> Direct -> Initial Fallback)를 부착하여 파비콘 미출력 현상을 종식시키고, 태그 추가 드롭다운의 넓이 조절(w-36) 및 빈 리스트 메시지, 전체 선택 해제 버튼을 추가 적용하여 우수한 기능적 성숙도 확충. - spinner.tsx: 전역 공통 로더 컴포넌트 추가: UI 전반의 비동기 호출 대기 상태 시 사용할 수 있도록 굵기와 테마 변경을 유연하게 수용하는 고품질 SVG 스피너 컴포넌트를 설계하여 사용자 대기 가독성 기여.
- network-and-infra.md: 인프라 문서 시각 정밀도 제고: 깨진 아키텍처 다이어그램 마크다운 링크 형식을 고화질 HTML img 엘리먼트로 보정하고, 인프라의 주요 수신 차단 격리 내용과 Real IP 복원 설정 등 최신 마이그레이션 변경 이력을 본문에 추가 기재하여 정보 최신성 준수. - 01. cloudflare-origin-shielding.md: 헤더 스푸핑 차단 문제 분석 보고서 신규 작성: 클라우드플레어 공식 IP를 통한 전용 포트 수신 화이트리스팅 과정, IP 가로채기(Header Spoofing) 위협 요인과 이를 극복하기 위한 Nginx 리얼 IP 복구 명령어 세팅 가이드라인을 상세 구성도로 표현하여 인프라 지식 자산화. - 02. nginx-unprivileged-and-healthcheck.md: 일반 계정 권한 적용 분석 보고서 신규 작성: Nginx 일반 계정 권한(unprivileged) 실행에 따른 포트 번호 80 충돌 원인과 8080 전환 원리, 도커 컴포즈 상의 프론트엔드 포트 포워딩 포트 조정 흐름, 그리고 Wget 명령어를 이용해 정밀한 컨테이너 생명 상태를 모니터링하는 노하우를 집약하여 기술 자료 확보.
10225b9 to
e3cfff9
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e3cfff951e
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| script: | | ||
| cd ~/Searchweb/deploy | ||
| # 기존 컨테이너 이름(relink-app, relink-nginx) 충돌 방지를 위해 선삭제 조치 | ||
| sudo docker rm -f relink-app relink-nginx || true |
There was a problem hiding this comment.
Avoid deleting running containers before image pull
Delete the pre-emptive docker rm -f step here; if Docker Hub is temporarily unavailable or compose pull fails, this workflow has already removed both production containers and causes immediate downtime until a manual recovery. docker compose up -d can recreate/replace containers without taking the service down first, so the current ordering turns transient pull failures into full outages.
Useful? React with 👍 / 👎.
| @@ -0,0 +1,81 @@ | |||
| server { | |||
| listen 8080; | |||
There was a problem hiding this comment.
Add HTTPS listener for the published 443 port
This config only listens on port 8080, but the production compose publishes 443:443 for the same container. As written, traffic to origin port 443 will be connection-refused because Nginx never binds that port in this file, so TLS-origin routing (or any direct HTTPS check) cannot work until a listen 443 ssl server block and cert wiring are added (or the 443 publish is removed).
Useful? React with 👍 / 👎.
0e38df7 to
e806ed8
Compare
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e806ed8c18
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| /** 컨텍스트 태그(빈도) 최대 길이 */ | ||
| private static final int MAX_CONTEXT_TAG_LENGTH = 80; | ||
| /** HIGH_CONTEXT 판정에 필요한 최소 커스텀 루트 폴더 수 */ | ||
| private static final int HIGH_CONTEXT_MIN_CUSTOM_ROOT_FOLDERS = 10; |
There was a problem hiding this comment.
Align HIGH_CONTEXT cutoff with intended 7-folder threshold
determineContextLevel now uses HIGH_CONTEXT_MIN_CUSTOM_ROOT_FOLDERS = 10, which classifies users with 7–9 custom root folders as LOW_CONTEXT even though this commit’s own context-mode spec and test intent treat 7+ as HIGH_CONTEXT (see the new context-level test expecting HIGH at 7 folders). In production this changes prompt policy for established users and can systematically bias folder recommendations toward new-folder creation instead of reuse.
Useful? React with 👍 / 👎.
e806ed8 to
3f608a5
Compare
💡 이슈
resolve {#45}
🤩 개요
🧑💻 작업 사항
🚀 1. CI/CD 자동 배포 시스템 구축 및 인프라 보안 강화
deploy-prod.yml,docker-compose-prod-web.yml):${{ github.sha }}고유 해시 태그 기반의 이미지 빌드/배포 자동화 및 SSH/SCP 라이브러리 버전 잠금 적용Dockerfile,nginx-prod.conf): 루트 권한 탈옥 위협(Container Escape) 방지를 위한nginx-unprivileged이미지 전환, 내부 포트 8080 개방 및 Wget 로컬 헬스체크 구현docker-compose-prod-db.yml,nginx-prod.conf,.dockerignore): DB 포트(7000) 사설망 차단으로 외부 노출 봉쇄, Cloudflare Real IP 복원 설정 추가, 민감 파일 빌드 누출 차단🔒 2. 백엔드 안정성 및 동시성 제어 적용
MemberFolderJpaDao.java,MemberFolderServiceImpl.java): 부모-자식 폴더 생성 및 삭제 정합성 보장을 위한 비관적 쓰기 락(PESSIMISTIC_WRITE) 적용JpaConfig.java): 로컬 OS 타임존 편차를 배제하고 UTC 표준 Clock 기반으로OffsetDateTime기록 규격 통일🎨 3. 프론트엔드 UI/UX 및 최적화
favicon.ts,SaveLinkDialog.tsx,RightPanel.tsx,spinner.tsx): API 오류 시 Direct/favicon.ico-> Initial Fallback으로 순차 전환되는 3단계 폴백 유틸 구현, SVG 스피너 컴포넌트 추가globals.css,layout.tsx,LandingHeader.tsx): 드래그 반전 및 깜빡임 방지(select-none) 도입, 구글 웹 폰트 사전 연결(preconnect/preload)로 로딩 성능 개선📖 4. 아키텍처 명세 및 가이드 추가
network-and-infra.md): 깨진 네트워크 아키텍처 다이어그램 복원 및 리얼 IP 복원 이력 반영docs/Troubleshooting/): Cloudflare 보안 화이트리스팅 및 Nginx 일반 계정 전환 관련 트러블슈팅 분석 문서 2종 신설📖 참고 사항
80:8080) 정상 확인access_log off예외 처리 반영Summary by CodeRabbit
릴리스 노트
New Features
Infrastructure
Documentation
Refactor