From 8895e09a58b8a4f299a95897793f527a1032c65b Mon Sep 17 00:00:00 2001 From: Minchan Kim <163628956+banchan01@users.noreply.github.com> Date: Tue, 27 Jan 2026 15:47:19 +0900 Subject: [PATCH 01/33] menu-bar-longstick-bug-fix (#560) --- frontend/src/pages/oj/components/NavBar.vue | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/frontend/src/pages/oj/components/NavBar.vue b/frontend/src/pages/oj/components/NavBar.vue index 31bf9bb1b..088cd76fb 100644 --- a/frontend/src/pages/oj/components/NavBar.vue +++ b/frontend/src/pages/oj/components/NavBar.vue @@ -150,21 +150,6 @@ export default { .menuItemText_community { cursor: pointer; } - - /deep/ .ivu-dropdown-item { - padding: 7px 7px; - text-align: center; - font-size: 14px !important; - font-weight: 550 !important; - } - - /deep/ .ivu-dropdown-item:hover { - color: #3c5977 !important; - } - - /deep/ .ivu-select-dropdown { - margin-top: 3px !important; - } } .modal { From 61af4b605d731c1ec378f124b0e9a44c0438d35b Mon Sep 17 00:00:00 2001 From: Minchan Kim <163628956+banchan01@users.noreply.github.com> Date: Tue, 27 Jan 2026 18:30:30 +0900 Subject: [PATCH 02/33] =?UTF-8?q?#562=20menu=20bar=20style=20=ED=83=9C?= =?UTF-8?q?=EA=B7=B8=20=ED=81=B4=EB=9E=98=EC=8A=A4=20=EC=88=98=EC=A0=95=20?= =?UTF-8?q?(#563)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * menu bar style 태그 클래스 수정 * community 드롭다운 menu class 추가 * style 제거 Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- frontend/src/pages/oj/components/NavBar.vue | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/frontend/src/pages/oj/components/NavBar.vue b/frontend/src/pages/oj/components/NavBar.vue index 088cd76fb..1f0b3670a 100644 --- a/frontend/src/pages/oj/components/NavBar.vue +++ b/frontend/src/pages/oj/components/NavBar.vue @@ -12,7 +12,7 @@ {{ $t("m.Community") }} - + {{ $t("m.Community_Free") }} {{ $t("m.Community_Question") }} @@ -150,6 +150,17 @@ export default { .menuItemText_community { cursor: pointer; } + + /deep/ .community-dropdown-menu .ivu-dropdown-item { + padding: 7px 7px; + text-align: center; + font-size: 14px !important; + font-weight: 550 !important; + } + + /deep/ .community-dropdown-menu .ivu-dropdown-item:hover { + color: #3c5977 !important; + } } .modal { From e877095077b1e6fe464ed2b1ac15f5736f8420b6 Mon Sep 17 00:00:00 2001 From: Junwoo Park <64635737+Boksam@users.noreply.github.com> Date: Sat, 31 Jan 2026 16:57:35 +0900 Subject: [PATCH 03/33] =?UTF-8?q?#559=20CI=20=ED=8C=8C=EC=9D=B4=ED=94=84?= =?UTF-8?q?=EB=9D=BC=EC=9D=B8=EC=97=90=EC=84=9C=20Kustomization=20?= =?UTF-8?q?=EC=9D=B4=EB=AF=B8=EC=A7=80=20=ED=83=9C=EA=B7=B8=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD=20(#565)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * specify images on kustomization.yaml * add tag commit on github action * fix typo & remove resolved todo comments * conditional needs & if for modify job * change hard-coded branch name using github.ref * use github.ref * replace kustomize to yq * resolve comments --- .github/workflows/ci2develop.yml | 52 +++++++++++++-- .../workflows/ci2production-and-release.yml | 63 +++++++++++++++---- kubernetes/base/backend/deployment.yaml | 4 +- kubernetes/base/celery-beat/deployment.yaml | 2 +- kubernetes/base/celery-worker/deployment.yaml | 2 +- kubernetes/base/frontend/deployment.yaml | 2 +- kubernetes/base/judge-server/deployment.yaml | 2 +- .../dev/backend-deployment-patch.yaml | 6 -- .../dev/celery-beat-deployment-patch.yaml | 5 -- .../dev/celery-worker-deployment-patch.yaml | 5 -- .../dev/frontend-deployment-patch.yaml | 1 - .../dev/judge-server-deployment-patch.yaml | 5 -- kubernetes/overlays/dev/kustomization.yaml | 14 +++++ .../prod/backend-deployment-patch.yaml | 5 -- .../prod/celery-beat-deployment-patch.yaml | 5 -- .../prod/celery-worker-deployment-patch.yaml | 5 -- .../prod/frontend-deployment-patch.yaml | 1 - .../prod/judge-server-deployment-patch.yaml | 5 -- kubernetes/overlays/prod/kustomization.yaml | 14 +++++ 19 files changed, 130 insertions(+), 68 deletions(-) diff --git a/.github/workflows/ci2develop.yml b/.github/workflows/ci2develop.yml index dec35d9c6..835e36ab6 100644 --- a/.github/workflows/ci2develop.yml +++ b/.github/workflows/ci2develop.yml @@ -6,6 +6,7 @@ on: - "deployment/**" - "scripts/**" - ".github/**" + - "kubernetes/**" branches: [develop] workflow_dispatch: @@ -53,9 +54,7 @@ jobs: context: ./backend file: ./backend/Dockerfile push: true - tags: | - ${{ secrets.HARBOR_REGISTRY }}/code-place-dev/backend:${{ github.sha }}-dev - ${{ secrets.HARBOR_REGISTRY }}/code-place-dev/backend:latest + tags: ${{ secrets.HARBOR_REGISTRY }}/code-place-dev/backend:${{ github.sha }}-dev ci-frontend-dev: needs: [detect-changes-by-component] @@ -81,9 +80,50 @@ jobs: context: ./frontend file: ./frontend/Dockerfile push: true - tags: | - ${{ secrets.HARBOR_REGISTRY }}/code-place-dev/frontend:${{ github.sha }}-dev - ${{ secrets.HARBOR_REGISTRY }}/code-place-dev/frontend:latest + tags: ${{ secrets.HARBOR_REGISTRY }}/code-place-dev/frontend:${{ github.sha }}-dev build-args: | SERVER_NAME=${{ secrets.DEV_SERVER_NAME }} APP_VERSION=${{ github.sha }}-dev + + update-dev-manifest: + needs: [ci-backend-dev, ci-frontend-dev] + if: always() && (needs.ci-backend-dev.result == 'success' || needs.ci-frontend-dev.result == 'success') + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + token: ${{ secrets.ACTION_TOKEN }} + fetch-depth: 0 + + # NOTE: 기존에 kustomize를 사용하였지만 포멧 문제로 yq로 변경 (junwoo) + # images.[] .name 에 맞는 항목의 newTag 값을 github.sha-dev로 변경 + - name: Update Backend Image Tag + if: needs.ci-backend-dev.result == 'success' + uses: mikefarah/yq@master + with: + cmd: yq -i '(.images[] | select(.name == "backend").newTag) = "${{ github.sha }}-dev"' kubernetes/overlays/dev/kustomization.yaml + + - name: Update Frontend Image Tag + if: needs.ci-frontend-dev.result == 'success' + uses: mikefarah/yq@master + with: + cmd: yq -i '(.images[] | select(.name == "frontend").newTag) = "${{ github.sha }}-dev"' kubernetes/overlays/dev/kustomization.yaml + + - name: Commit and push changes + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git add kubernetes/overlays/dev/kustomization.yaml + + # 변경사항이 없으면 종료 + if git diff-index --quiet HEAD; then + echo "No changes to commit" + exit 0 + fi + + # 충돌 방지를 위해 최신 develop 브랜치로 rebase 후 push + # [skip ci] 태그를 커밋 메시지에 추가하여 추가 CI 실행을 방지 + git commit -m "ci: Update dev image tags to ${{ github.sha }} [skip ci]" + git pull --rebase origin ${{ github.ref_name }} + git push origin ${{ github.ref_name }} diff --git a/.github/workflows/ci2production-and-release.yml b/.github/workflows/ci2production-and-release.yml index 6937c5b93..2c8a618b3 100644 --- a/.github/workflows/ci2production-and-release.yml +++ b/.github/workflows/ci2production-and-release.yml @@ -5,6 +5,7 @@ on: paths-ignore: - "deployment/**" - "scripts/**" + - "kubernetes/**" branches: [main] workflow_dispatch: @@ -81,9 +82,7 @@ jobs: context: ./backend file: ./backend/Dockerfile push: true - tags: | - ${{ secrets.HARBOR_REGISTRY }}/code-place-prod/backend:${{ github.sha }}-prod - ${{ secrets.HARBOR_REGISTRY }}/code-place-prod/backend:latest + tags: ${{ secrets.HARBOR_REGISTRY }}/code-place-prod/backend:${{ github.sha }}-prod ci-frontend: needs: [detect-changes-by-component] @@ -109,9 +108,7 @@ jobs: context: ./frontend file: ./frontend/Dockerfile push: true - tags: | - ${{ secrets.HARBOR_REGISTRY }}/code-place-prod/frontend:${{ github.sha }}-prod - ${{ secrets.HARBOR_REGISTRY }}/code-place-prod/frontend:latest + tags: ${{ secrets.HARBOR_REGISTRY }}/code-place-prod/frontend:${{ github.sha }}-prod build-args: | SERVER_NAME=${{ secrets.PROD_SERVER_NAME }} APP_VERSION=${{ github.sha }} @@ -140,17 +137,59 @@ jobs: context: ./hub/auth_server file: ./hub/auth_server/Dockerfile push: true - tags: | - ${{ secrets.HARBOR_REGISTRY }}/code-place-hub/auth-server:${{ github.sha }} - ${{ secrets.HARBOR_REGISTRY }}/code-place-hub/auth-server:latest + tags: ${{ secrets.HARBOR_REGISTRY }}/code-place-hub/auth-server:${{ github.sha }} + + update-prod-manifest: + needs: [check-release-branch, ci-backend, ci-frontend] + if: | + always() && + needs.check-release-branch.outputs.is_release == 'true' && + (needs.ci-frontend.result == 'success' || needs.ci-backend.result == 'success') + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v2 + with: + token: ${{ secrets.ACTION_TOKEN }} + fetch-depth: 0 + + # NOTE: 기존에 kustomize를 사용하였지만 포멧 문제로 yq로 변경 (junwoo) + # images.[] .name 에 맞는 항목의 newTag 값을 github.sha 로 변경 + - name: Update Backend Image Tag + if: needs.ci-backend.result == 'success' + uses: mikefarah/yq@master + with: + cmd: yq -i '(.images[] | select(.name == "backend").newTag) = "${{ github.sha }}-prod"' kubernetes/overlays/prod/kustomization.yaml + + - name: Update Frontend Image Tag + if: needs.ci-frontend.result == 'success' + uses: mikefarah/yq@master + with: + cmd: yq -i '(.images[] | select(.name == "frontend").newTag) = "${{ github.sha }}-prod"' kubernetes/overlays/prod/kustomization.yaml + + - name: Commit and push changes + run: | + git config --global user.name 'github-actions[bot]' + git config --global user.email 'github-actions[bot]@users.noreply.github.com' + git add kubernetes/overlays/prod/kustomization.yaml + + # 변경사항이 없으면 종료 + if git diff-index --quiet HEAD; then + echo "No changes to commit" + exit 0 + fi + + # [skip ci] 태그를 커밋 메시지에 추가하여 추가 CI 실행을 방지 + git commit -m "ci: Update prod image tags to ${{ github.sha }} [skip ci]" + git pull --rebase origin main + git push origin main create-release: - needs: [check-release-branch, ci-frontend, ci-backend] + needs: [check-release-branch, update-prod-manifest] if: | always() && needs.check-release-branch.outputs.is_release == 'true' && - (needs.ci-frontend.result == 'success' || needs.ci-frontend.result == 'skipped') && - (needs.ci-backend.result == 'success' || needs.ci-backend.result == 'skipped') + (needs.update-prod-manifest.result == 'success' || needs.update-prod-manifest.result == 'skipped') runs-on: ubuntu-latest steps: - name: Checkout Code diff --git a/kubernetes/base/backend/deployment.yaml b/kubernetes/base/backend/deployment.yaml index eed8e3dec..7ce8c5065 100644 --- a/kubernetes/base/backend/deployment.yaml +++ b/kubernetes/base/backend/deployment.yaml @@ -24,9 +24,7 @@ spec: - name: regcred containers: - name: backend - # TODO: 현재는 Docker Swarm에서 사용하는 방식을 그대로 가져와 latest 로 매번 받아오고 있습니다. - # 하지만, 매번 pull 하는 것은 비효율적이므로 특정 버전을 설정한 후 IfNotPresent 로 받아오도록 변경해야 합니다. - image: harbor.code-place-dev.site/code-place-dev/backend:latest + image: backend:latest imagePullPolicy: Always command: ["/app/deploy/entrypoint.sh"] ports: diff --git a/kubernetes/base/celery-beat/deployment.yaml b/kubernetes/base/celery-beat/deployment.yaml index e8016d2da..c96791cc5 100644 --- a/kubernetes/base/celery-beat/deployment.yaml +++ b/kubernetes/base/celery-beat/deployment.yaml @@ -21,7 +21,7 @@ spec: - name: regcred containers: - name: celery-beat - image: registry.code-place-dev.site/code-place-dev/backend:latest + image: backend:latest imagePullPolicy: IfNotPresent command: ["/app/deploy/entrypoint_beat.sh"] resources: diff --git a/kubernetes/base/celery-worker/deployment.yaml b/kubernetes/base/celery-worker/deployment.yaml index 329ccffc0..5aa1a2556 100644 --- a/kubernetes/base/celery-worker/deployment.yaml +++ b/kubernetes/base/celery-worker/deployment.yaml @@ -24,7 +24,7 @@ spec: - name: regcred containers: - name: celery-worker - image: registry.code-place-dev.site/code-place-dev/backend:latest + image: backend:latest imagePullPolicy: IfNotPresent command: ["/app/deploy/entrypoint_worker.sh"] resources: diff --git a/kubernetes/base/frontend/deployment.yaml b/kubernetes/base/frontend/deployment.yaml index da93e2ad6..1f5372e91 100644 --- a/kubernetes/base/frontend/deployment.yaml +++ b/kubernetes/base/frontend/deployment.yaml @@ -24,7 +24,7 @@ spec: - name: regcred containers: - name: frontend - image: registry.code-place-dev.site/code-place-dev/frontend:latest + image: frontend:latest imagePullPolicy: Always ports: - containerPort: 8000 diff --git a/kubernetes/base/judge-server/deployment.yaml b/kubernetes/base/judge-server/deployment.yaml index 15db43def..efb1b272e 100644 --- a/kubernetes/base/judge-server/deployment.yaml +++ b/kubernetes/base/judge-server/deployment.yaml @@ -19,7 +19,7 @@ spec: - name: regcred containers: - name: judge-server - image: registry.code-place-dev.site/code-place-dev/judge-server:1.0.3 + image: judge-server:latest imagePullPolicy: IfNotPresent securityContext: capabilities: diff --git a/kubernetes/overlays/dev/backend-deployment-patch.yaml b/kubernetes/overlays/dev/backend-deployment-patch.yaml index 5bcda0787..1c4dcc7d2 100644 --- a/kubernetes/overlays/dev/backend-deployment-patch.yaml +++ b/kubernetes/overlays/dev/backend-deployment-patch.yaml @@ -2,9 +2,3 @@ apiVersion: apps/v1 kind: Deployment metadata: name: backend -spec: - template: - spec: - containers: - - name: backend - image: harbor.code-place-dev.site/code-place-dev/backend:latest diff --git a/kubernetes/overlays/dev/celery-beat-deployment-patch.yaml b/kubernetes/overlays/dev/celery-beat-deployment-patch.yaml index fac536813..2d12b1d5d 100644 --- a/kubernetes/overlays/dev/celery-beat-deployment-patch.yaml +++ b/kubernetes/overlays/dev/celery-beat-deployment-patch.yaml @@ -8,8 +8,3 @@ spec: replicas: 1 strategy: type: Recreate - template: - spec: - containers: - - name: celery-beat - image: harbor.code-place-dev.site/code-place-dev/backend:latest diff --git a/kubernetes/overlays/dev/celery-worker-deployment-patch.yaml b/kubernetes/overlays/dev/celery-worker-deployment-patch.yaml index 628539bee..7142506e6 100644 --- a/kubernetes/overlays/dev/celery-worker-deployment-patch.yaml +++ b/kubernetes/overlays/dev/celery-worker-deployment-patch.yaml @@ -11,8 +11,3 @@ spec: rollingUpdate: maxUnavailable: 0 maxSurge: 1 - template: - spec: - containers: - - name: celery-worker - image: harbor.code-place-dev.site/code-place-dev/backend:latest diff --git a/kubernetes/overlays/dev/frontend-deployment-patch.yaml b/kubernetes/overlays/dev/frontend-deployment-patch.yaml index a6ce04345..254006267 100644 --- a/kubernetes/overlays/dev/frontend-deployment-patch.yaml +++ b/kubernetes/overlays/dev/frontend-deployment-patch.yaml @@ -7,7 +7,6 @@ spec: spec: containers: - name: frontend - image: harbor.code-place-dev.site/code-place-dev/frontend:latest env: - name: FORCE_HTTPS value: "0" diff --git a/kubernetes/overlays/dev/judge-server-deployment-patch.yaml b/kubernetes/overlays/dev/judge-server-deployment-patch.yaml index 65a0a2094..9f8c73c7e 100644 --- a/kubernetes/overlays/dev/judge-server-deployment-patch.yaml +++ b/kubernetes/overlays/dev/judge-server-deployment-patch.yaml @@ -11,8 +11,3 @@ spec: rollingUpdate: maxUnavailable: 1 maxSurge: 1 - template: - spec: - containers: - - name: judge-server - image: harbor.code-place-dev.site/code-place-dev/judge-server:1.0.3 diff --git a/kubernetes/overlays/dev/kustomization.yaml b/kubernetes/overlays/dev/kustomization.yaml index e123ae0aa..d96105849 100644 --- a/kubernetes/overlays/dev/kustomization.yaml +++ b/kubernetes/overlays/dev/kustomization.yaml @@ -15,3 +15,17 @@ patches: - path: celery-worker-deployment-patch.yaml - path: celery-beat-deployment-patch.yaml - path: judge-server-deployment-patch.yaml + +# NOTE: base 디렉토리의 이미지 이름과 태그를 오버라이드합니다. +# `backend`, `frontend` 이미지 태그는 CI 파이프라인에서 자동으로 변경됩니다. +# `judge-server` 이미지는 자주 변경되지 않으므로 수동으로 설정합니다. +images: + - name: backend + newName: harbor.code-place-dev.site/code-place-dev/backend + newTag: eb40822b5b950bf75f00ed053609724a2a10be7f-dev + - name: frontend + newName: harbor.code-place-dev.site/code-place-dev/frontend + newTag: 61af4b605d731c1ec378f124b0e9a44c0438d35b-dev + - name: judge-server + newName: harbor.code-place-dev.site/code-place-dev/judge-server + newTag: 1.0.3 diff --git a/kubernetes/overlays/prod/backend-deployment-patch.yaml b/kubernetes/overlays/prod/backend-deployment-patch.yaml index da9014e81..f355c8031 100644 --- a/kubernetes/overlays/prod/backend-deployment-patch.yaml +++ b/kubernetes/overlays/prod/backend-deployment-patch.yaml @@ -9,8 +9,3 @@ spec: rollingUpdate: maxUnavailable: 2 maxSurge: 2 - template: - spec: - containers: - - name: backend - image: harbor.code-place-dev.site/code-place-prod/backend:latest diff --git a/kubernetes/overlays/prod/celery-beat-deployment-patch.yaml b/kubernetes/overlays/prod/celery-beat-deployment-patch.yaml index 704d906fa..2d12b1d5d 100644 --- a/kubernetes/overlays/prod/celery-beat-deployment-patch.yaml +++ b/kubernetes/overlays/prod/celery-beat-deployment-patch.yaml @@ -8,8 +8,3 @@ spec: replicas: 1 strategy: type: Recreate - template: - spec: - containers: - - name: celery-beat - image: harbor.code-place-dev.site/code-place-prod/backend:latest diff --git a/kubernetes/overlays/prod/celery-worker-deployment-patch.yaml b/kubernetes/overlays/prod/celery-worker-deployment-patch.yaml index d9587b4c1..3d3bcc7d9 100644 --- a/kubernetes/overlays/prod/celery-worker-deployment-patch.yaml +++ b/kubernetes/overlays/prod/celery-worker-deployment-patch.yaml @@ -11,8 +11,3 @@ spec: rollingUpdate: maxUnavailable: 1 maxSurge: 1 - template: - spec: - containers: - - name: celery-worker - image: harbor.code-place-dev.site/code-place-prod/backend:latest diff --git a/kubernetes/overlays/prod/frontend-deployment-patch.yaml b/kubernetes/overlays/prod/frontend-deployment-patch.yaml index 99e7a9a76..026f734c9 100644 --- a/kubernetes/overlays/prod/frontend-deployment-patch.yaml +++ b/kubernetes/overlays/prod/frontend-deployment-patch.yaml @@ -13,7 +13,6 @@ spec: spec: containers: - name: frontend - image: harbor.code-place-dev.site/code-place-prod/frontend:latest env: - name: FORCE_HTTPS value: "0" diff --git a/kubernetes/overlays/prod/judge-server-deployment-patch.yaml b/kubernetes/overlays/prod/judge-server-deployment-patch.yaml index f9ab30609..a24b607fa 100644 --- a/kubernetes/overlays/prod/judge-server-deployment-patch.yaml +++ b/kubernetes/overlays/prod/judge-server-deployment-patch.yaml @@ -11,8 +11,3 @@ spec: rollingUpdate: maxUnavailable: 2 maxSurge: 2 - template: - spec: - containers: - - name: judge-server - image: harbor.code-place-dev.site/code-place-prod/judge-server:1.0.3 diff --git a/kubernetes/overlays/prod/kustomization.yaml b/kubernetes/overlays/prod/kustomization.yaml index a699275e4..78fbad6fc 100644 --- a/kubernetes/overlays/prod/kustomization.yaml +++ b/kubernetes/overlays/prod/kustomization.yaml @@ -15,3 +15,17 @@ patches: - path: celery-worker-deployment-patch.yaml - path: celery-beat-deployment-patch.yaml - path: judge-server-deployment-patch.yaml + +# NOTE: base 디렉토리의 이미지 이름과 태그를 오버라이드합니다. +# `backend`, `frontend` 이미지 태그는 CI 파이프라인에서 자동으로 변경됩니다. +# `judge-server` 이미지는 자주 변경되지 않으므로 수동으로 설정합니다. +images: + - name: backend + newName: harbor.code-place-dev.site/code-place-prod/backend + newTag: 3a65dc653fc6fb3050a04e373bbf58e7d391c762-prod + - name: frontend + newName: harbor.code-place-dev.site/code-place-prod/frontend + newTag: 3a65dc653fc6fb3050a04e373bbf58e7d391c762-prod + - name: judge-server + newName: harbor.code-place-dev.site/code-place-prod/judge-server + newTag: 1.0.3 From 43280658e59b0a8c6ddcd6ba89e0f2850fbf6dfb Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 5 Feb 2026 19:09:36 +0900 Subject: [PATCH 04/33] add service-architecture-docs --- .../infra/service-architecture/_index.md | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 docs/content/developer/infra/service-architecture/_index.md diff --git a/docs/content/developer/infra/service-architecture/_index.md b/docs/content/developer/infra/service-architecture/_index.md new file mode 100644 index 000000000..071fe8281 --- /dev/null +++ b/docs/content/developer/infra/service-architecture/_index.md @@ -0,0 +1,85 @@ +--- +title: "서비스 아키텍쳐" +date: 2026-02-05T18:55:49+09:00 +draft: false +weight: 1 +--- + +{{< callout >}} +이 문서에서는 CodePlace의 전체적인 서비스 아키텍쳐와 트래픽 처리 흐름에 대해 설명합니다. +{{< /callout >}} + +## 서비스 아키텍쳐 + +### 1. Entry Point + +서비스의 모든 요청 트래픽은 가장 먼저 `Traefik`을 거치게 됩니다. CodePlace에서 `Traefik`은 크게 두 가지 핵심적인 네트워크 처리를 담당합니다. + +- 보안을 위해 HTTP(80) 요청을 강제로 HTTPS(443)로 Redirect합니다. +- 외부에서 암호화되어 들어온 패킷을 여기서 복호화(TLS Termination)하여 내부망으로 전달합니다. + +덕분에 내부 컨테이너들은 복잡한 암호화/복호화 연산 없이 효율적으로 통신할 수 있습니다. `Traefik`은 위 과정을 거친 모든 요청을 Frontend 컨테이너 (`Nginx`)로 전달합니다. + +### 2. Routing + +`Traefik`에서 전달된 요청은 Frontend 컨테이너의 `Nginx`에 도달하며, URL 경로에 따라 적절한 서비스로 분기됩니다. + +### 1) 페이지 요청 (/) + +루트 경로 등 페이지 관련 요청이 들어오면, Nginx는 사전에 빌드된 정적 파일을 브라우저에게 서빙합니다. +이 과정을 이해하기 위해서는 아래의 내용을 이해하여야 합니다. + +- Build Process +Frontend 서버의 궁극적인 목표는 **정적파일 서빙**입니다. **배포 전 단계에서** 수행되는, 코드베이스에서 여러 가지로 흩어져 있는 소스코드를 하나로 묶는 과정이 빌드입니다. Build는 Node.js 환경에서 `Webpack`을 통해서 수행됩니다. 빌드는 **코드 난독화, 압축, 번들링** 등 여러 가지를 포함하는데, 그중 JavaScript 버전 문법 호환성은 `Babel`을 통해 해결합니다. +빌드 과정을 거친 결과물(`vendor.js`, **`index.html`**등)은 **nginx 폴더** 안에 저장이 됩니다. 이 결과물을 Nginx가 브라우저에 서빙하는 것입니다. + +- SPA (Single Page Application) +Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이지 애플리케이션**으로, MPA와 대비됩니다. 비교를 위해 `MPA`를 설명하겠습니다. `MPA`는 Multi Page Application으로서, 페이지 이동 시마다 서버에 정적파일을 새로 요청하여 브라우저에 렌더링 하는 방식입니다. +반면 `SPA`는 최초 접속 시, 위의 빌드과정을 통해 사전에 만들어진 애플리케이션 구동에 필요한 모든 자원(`vendor.js`, **`index.html`** 등)을 한 번에 로드합니다. +이때 모든 정적파일을 모두 다 하나의 파일에 담지는 않습니다. 초기 로딩 속도 최적화를 위해 자주 쓰이지 않거나 무거운 기능은 **Chunk** 단위로 잘라두었다가, 실제 해당 기능이 필요할 때, 브라우저가 요청하여 로드하도록 설계되어 있습니다. +이후 페이지 전환 시에는, 이미 서빙된 **index.html** 파일 위에서 사전에 로드된 혹은 필요 시 Chunk로 로드된 `vendor.js` 등의 자바스크립트가 컴포넌트만 교체하여 화면 전환을 수행합니다. 이를 통해 서버 요청을 최소화하여 MPA에 비해 빠른 사용성을 제공합니다. + +이러한 빌드, SPA 방식을 통해서 화면 로드가 일어납니다. + +### 2) API 요청 (/api) + +`/api`로 시작하는 경로로 들어온 요청일 경우, Nginx는 이를 뒷단의 **Django 서버**로 프록시합니다. 전달받은 요청에 따라 Django 서버는 매핑된 URL 엔드포인트를 통해 비즈니스 로직 (DB 조회, 연산 등)을 수행한 후, 그 결과를 브라우저에게 반환합니다. + +### 3. Celery + +`Celery`는 분산 비동기 작업 큐 프레임워크로서, `Celery Beat`와 `Celery Worker`로 구성됩니다. +백엔드 로직 중 실시간성이 필요하지 않거나 리소스를 많이 점유하는 작업은 Redis를 사용하여 비동기로 처리합니다. 주로 *채점*, *이메일 발송*, *세션 관리*, 그리고 *등수/점수 업데이트와 같은 스케줄링 Job*이 여기에 해당합니다. +이 구조의 이해를 돕기 위해 Celery의 두 핵심 컴포넌트를 설명하겠습니다. + +- **Celery Worker :** Redis 큐에 쌓인 작업을 실제로 가져가서 수행하는 주체입니다. Django 서버와 실행환경은 유사하지만, Redis 큐에 있는 작업만 처리합니다. +- **Celery Beat :** 정해진 시간마다 주기적으로 실행되어야 하는 작업을 Redis 큐에 자동으로 예약해주는 스케줄러입니다. + +**[동작 프로세스 예시]** + +1. **문제 제출 (`Celery Worker`)** +사용자가 '제출하기' 버튼을 누르면, Django 서버는 직접 채점을 수행하지 않고 **채점 요청 메시지**를 `Redis Queue`에 등록만 하고 즉시 응답을 반환합니다. 대기 중이던 `Celery Worker`가 Redis Queue의 job을 감지하여 가져간 뒤 별도의 격리된 채점 서버와 통신하며 채점을 진행합니다. + +2. **통계 업데이트 (`Celery Beat`)** +사용자가 직접 요청하지 않아도 시스템이 수행해야 하는 '랭킹 업데이트' 같은 작업은 `Celery Beat`가 담당합니다. `Celery Beat`는 설정된 시각이 되면 자동으로 Redis에 작업 요청을 넣고, `Celery Worker`가 이를 가져가서 점수를 갱신합니다. + +`Celery Worker`는 Django 서버와 본질적으로 유사한 환경(동일한 코드베이스, ORM 사용)을 가집니다.  다만 가장 큰 차이점은 수신 대상이 다르다는 것입니다. + +- **Django Server :**  `HTTP Port`로 유입되는 클라이언트의 요청을 수신하여 처리합니다. +- **Celery Worker :** `Redis`에 등록된 비동기 작업을 대기 상태로 감시하다가 새로운 작업이 들어오면 이를 가져와 처리합니다. + +**왜 이렇게 나누었을까?** + +만약 Celery 없이 Django가 모든 작업을 직접 처리한다고 가정해봅시다. + +채점 요청이 동시에 1,000개 이상 들어오는 상황에서, CPU 연산량이 크고 실행 시간이 긴 **채점 작업**을 Django 서버 프로세스 안에서 수행하게 되면 요청 처리 흐름이 지연됩니다. 그 결과 로그인, 페이지 조회와 같은 일반적인 사용자 요청까지 영향을 받아 서비스 응답성이 급격히 저하될 수 있습니다. + +물론 Django 내부에서도 비동기 처리를 구현할 수는 있습니다. +하지만 채점과 같이 **CPU 사용량이 크고 오래 걸리는 작업**을 HTTP 요청 처리 흐름과 함께 수행할 경우, 여전히 다른 사용자 요청에 영향을 주는 구조적 한계가 존재합니다. + +따라서 CodePlace는 이러한 문제를 방지하기 위해, 무거운 작업을 Django 서버와 분리된 별도의 실행 주체인 `Celery Worker`에서 처리하도록 설계하였습니다. + +### 4. Container + +`Frontend 서버`, `Backend 서버`, `Redis`, `PostgreSQL`, `Celery Worker`, `Celery Beat`, `Judge Server` 모두 **Docker Container** 기반으로 운용됩니다. + +이를 통해 각 서비스의 실행 환경을 격리시켜 충돌을 방지하고, `docker-compose`를 이용해 각 컨테이너를 일관성 있게 배포하고 관리할 수 있는 확장 가능한 구조를 갖췄습니다. From 0b034744504422ef9776af58c128d0a41493e609 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 5 Feb 2026 19:13:15 +0900 Subject: [PATCH 05/33] edit service-architecture-docs --- docs/content/developer/infra/service-architecture/_index.md | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/docs/content/developer/infra/service-architecture/_index.md b/docs/content/developer/infra/service-architecture/_index.md index 071fe8281..04ced1d5c 100644 --- a/docs/content/developer/infra/service-architecture/_index.md +++ b/docs/content/developer/infra/service-architecture/_index.md @@ -1,16 +1,14 @@ --- -title: "서비스 아키텍쳐" date: 2026-02-05T18:55:49+09:00 draft: false +title: "Service Architecture" weight: 1 --- {{< callout >}} -이 문서에서는 CodePlace의 전체적인 서비스 아키텍쳐와 트래픽 처리 흐름에 대해 설명합니다. +이 문서에서는 CodePlace의 전체적인 Service Architecture와 트래픽 처리 흐름에 대해 설명합니다. {{< /callout >}} -## 서비스 아키텍쳐 - ### 1. Entry Point 서비스의 모든 요청 트래픽은 가장 먼저 `Traefik`을 거치게 됩니다. CodePlace에서 `Traefik`은 크게 두 가지 핵심적인 네트워크 처리를 담당합니다. From d7bafc38524acdb83e0d1113b70d349e11f4e731 Mon Sep 17 00:00:00 2001 From: Minchan Kim <163628956+banchan01@users.noreply.github.com> Date: Thu, 5 Feb 2026 19:16:59 +0900 Subject: [PATCH 06/33] add service-architecture image Added an image and section header for CodePlace Service Architecture. --- docs/content/developer/infra/service-architecture/_index.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/content/developer/infra/service-architecture/_index.md b/docs/content/developer/infra/service-architecture/_index.md index 04ced1d5c..fb4c73d1b 100644 --- a/docs/content/developer/infra/service-architecture/_index.md +++ b/docs/content/developer/infra/service-architecture/_index.md @@ -9,6 +9,10 @@ weight: 1 이 문서에서는 CodePlace의 전체적인 Service Architecture와 트래픽 처리 흐름에 대해 설명합니다. {{< /callout >}} +### 0. CodePlace Service-Architecture +image + + ### 1. Entry Point 서비스의 모든 요청 트래픽은 가장 먼저 `Traefik`을 거치게 됩니다. CodePlace에서 `Traefik`은 크게 두 가지 핵심적인 네트워크 처리를 담당합니다. From 792425c91399ff1cb04541c57c363151a43c87be Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 5 Feb 2026 19:23:21 +0900 Subject: [PATCH 07/33] edit service-architecture-docs --- .../infra/service-architecture/_index.md | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/docs/content/developer/infra/service-architecture/_index.md b/docs/content/developer/infra/service-architecture/_index.md index fb4c73d1b..3337eb6d0 100644 --- a/docs/content/developer/infra/service-architecture/_index.md +++ b/docs/content/developer/infra/service-architecture/_index.md @@ -12,6 +12,7 @@ weight: 1 ### 0. CodePlace Service-Architecture image +--- ### 1. Entry Point @@ -22,6 +23,8 @@ weight: 1 덕분에 내부 컨테이너들은 복잡한 암호화/복호화 연산 없이 효율적으로 통신할 수 있습니다. `Traefik`은 위 과정을 거친 모든 요청을 Frontend 컨테이너 (`Nginx`)로 전달합니다. +--- + ### 2. Routing `Traefik`에서 전달된 요청은 Frontend 컨테이너의 `Nginx`에 도달하며, URL 경로에 따라 적절한 서비스로 분기됩니다. @@ -35,9 +38,11 @@ weight: 1 Frontend 서버의 궁극적인 목표는 **정적파일 서빙**입니다. **배포 전 단계에서** 수행되는, 코드베이스에서 여러 가지로 흩어져 있는 소스코드를 하나로 묶는 과정이 빌드입니다. Build는 Node.js 환경에서 `Webpack`을 통해서 수행됩니다. 빌드는 **코드 난독화, 압축, 번들링** 등 여러 가지를 포함하는데, 그중 JavaScript 버전 문법 호환성은 `Babel`을 통해 해결합니다. 빌드 과정을 거친 결과물(`vendor.js`, **`index.html`**등)은 **nginx 폴더** 안에 저장이 됩니다. 이 결과물을 Nginx가 브라우저에 서빙하는 것입니다. +
+ - SPA (Single Page Application) Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이지 애플리케이션**으로, MPA와 대비됩니다. 비교를 위해 `MPA`를 설명하겠습니다. `MPA`는 Multi Page Application으로서, 페이지 이동 시마다 서버에 정적파일을 새로 요청하여 브라우저에 렌더링 하는 방식입니다. -반면 `SPA`는 최초 접속 시, 위의 빌드과정을 통해 사전에 만들어진 애플리케이션 구동에 필요한 모든 자원(`vendor.js`, **`index.html`** 등)을 한 번에 로드합니다. +반면 `SPA`는 최초 접속 시, 위의 빌드과정을 통해 사전에 만들어진 애플리케이션 구동에 필요한 모든 자원(`vendor.js`, `index.html` 등)을 한 번에 로드합니다. 이때 모든 정적파일을 모두 다 하나의 파일에 담지는 않습니다. 초기 로딩 속도 최적화를 위해 자주 쓰이지 않거나 무거운 기능은 **Chunk** 단위로 잘라두었다가, 실제 해당 기능이 필요할 때, 브라우저가 요청하여 로드하도록 설계되어 있습니다. 이후 페이지 전환 시에는, 이미 서빙된 **index.html** 파일 위에서 사전에 로드된 혹은 필요 시 Chunk로 로드된 `vendor.js` 등의 자바스크립트가 컴포넌트만 교체하여 화면 전환을 수행합니다. 이를 통해 서버 요청을 최소화하여 MPA에 비해 빠른 사용성을 제공합니다. @@ -47,6 +52,8 @@ Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이 `/api`로 시작하는 경로로 들어온 요청일 경우, Nginx는 이를 뒷단의 **Django 서버**로 프록시합니다. 전달받은 요청에 따라 Django 서버는 매핑된 URL 엔드포인트를 통해 비즈니스 로직 (DB 조회, 연산 등)을 수행한 후, 그 결과를 브라우저에게 반환합니다. +--- + ### 3. Celery `Celery`는 분산 비동기 작업 큐 프레임워크로서, `Celery Beat`와 `Celery Worker`로 구성됩니다. @@ -56,6 +63,8 @@ Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이 - **Celery Worker :** Redis 큐에 쌓인 작업을 실제로 가져가서 수행하는 주체입니다. Django 서버와 실행환경은 유사하지만, Redis 큐에 있는 작업만 처리합니다. - **Celery Beat :** 정해진 시간마다 주기적으로 실행되어야 하는 작업을 Redis 큐에 자동으로 예약해주는 스케줄러입니다. +
+ **[동작 프로세스 예시]** 1. **문제 제출 (`Celery Worker`)** @@ -69,6 +78,8 @@ Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이 - **Django Server :**  `HTTP Port`로 유입되는 클라이언트의 요청을 수신하여 처리합니다. - **Celery Worker :** `Redis`에 등록된 비동기 작업을 대기 상태로 감시하다가 새로운 작업이 들어오면 이를 가져와 처리합니다. +
+ **왜 이렇게 나누었을까?** 만약 Celery 없이 Django가 모든 작업을 직접 처리한다고 가정해봅시다. @@ -80,6 +91,8 @@ Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이 따라서 CodePlace는 이러한 문제를 방지하기 위해, 무거운 작업을 Django 서버와 분리된 별도의 실행 주체인 `Celery Worker`에서 처리하도록 설계하였습니다. +--- + ### 4. Container `Frontend 서버`, `Backend 서버`, `Redis`, `PostgreSQL`, `Celery Worker`, `Celery Beat`, `Judge Server` 모두 **Docker Container** 기반으로 운용됩니다. From 52dc5a3a5327803b590e6ddb468e647c36f8366b Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 5 Feb 2026 19:26:29 +0900 Subject: [PATCH 08/33] update format in service-architecture --- docs/content/developer/infra/service-architecture/_index.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/content/developer/infra/service-architecture/_index.md b/docs/content/developer/infra/service-architecture/_index.md index 3337eb6d0..5b6a99742 100644 --- a/docs/content/developer/infra/service-architecture/_index.md +++ b/docs/content/developer/infra/service-architecture/_index.md @@ -35,12 +35,14 @@ weight: 1 이 과정을 이해하기 위해서는 아래의 내용을 이해하여야 합니다. - Build Process +
Frontend 서버의 궁극적인 목표는 **정적파일 서빙**입니다. **배포 전 단계에서** 수행되는, 코드베이스에서 여러 가지로 흩어져 있는 소스코드를 하나로 묶는 과정이 빌드입니다. Build는 Node.js 환경에서 `Webpack`을 통해서 수행됩니다. 빌드는 **코드 난독화, 압축, 번들링** 등 여러 가지를 포함하는데, 그중 JavaScript 버전 문법 호환성은 `Babel`을 통해 해결합니다. 빌드 과정을 거친 결과물(`vendor.js`, **`index.html`**등)은 **nginx 폴더** 안에 저장이 됩니다. 이 결과물을 Nginx가 브라우저에 서빙하는 것입니다.
- SPA (Single Page Application) +
Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이지 애플리케이션**으로, MPA와 대비됩니다. 비교를 위해 `MPA`를 설명하겠습니다. `MPA`는 Multi Page Application으로서, 페이지 이동 시마다 서버에 정적파일을 새로 요청하여 브라우저에 렌더링 하는 방식입니다. 반면 `SPA`는 최초 접속 시, 위의 빌드과정을 통해 사전에 만들어진 애플리케이션 구동에 필요한 모든 자원(`vendor.js`, `index.html` 등)을 한 번에 로드합니다. 이때 모든 정적파일을 모두 다 하나의 파일에 담지는 않습니다. 초기 로딩 속도 최적화를 위해 자주 쓰이지 않거나 무거운 기능은 **Chunk** 단위로 잘라두었다가, 실제 해당 기능이 필요할 때, 브라우저가 요청하여 로드하도록 설계되어 있습니다. @@ -56,7 +58,7 @@ Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이 ### 3. Celery -`Celery`는 분산 비동기 작업 큐 프레임워크로서, `Celery Beat`와 `Celery Worker`로 구성됩니다. +`Celery`는 비동기 작업 큐 프레임워크로서, `Celery Beat`와 `Celery Worker`로 구성됩니다. 백엔드 로직 중 실시간성이 필요하지 않거나 리소스를 많이 점유하는 작업은 Redis를 사용하여 비동기로 처리합니다. 주로 *채점*, *이메일 발송*, *세션 관리*, 그리고 *등수/점수 업데이트와 같은 스케줄링 Job*이 여기에 해당합니다. 이 구조의 이해를 돕기 위해 Celery의 두 핵심 컴포넌트를 설명하겠습니다. From f0522362d2ae8e4bc72f5d5644d7cb4e54aaed2b Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 5 Feb 2026 19:27:50 +0900 Subject: [PATCH 09/33] update style in service-architecture --- docs/content/developer/infra/service-architecture/_index.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/content/developer/infra/service-architecture/_index.md b/docs/content/developer/infra/service-architecture/_index.md index 5b6a99742..6995a69e6 100644 --- a/docs/content/developer/infra/service-architecture/_index.md +++ b/docs/content/developer/infra/service-architecture/_index.md @@ -34,14 +34,14 @@ weight: 1 루트 경로 등 페이지 관련 요청이 들어오면, Nginx는 사전에 빌드된 정적 파일을 브라우저에게 서빙합니다. 이 과정을 이해하기 위해서는 아래의 내용을 이해하여야 합니다. -- Build Process +- **Build Process**
Frontend 서버의 궁극적인 목표는 **정적파일 서빙**입니다. **배포 전 단계에서** 수행되는, 코드베이스에서 여러 가지로 흩어져 있는 소스코드를 하나로 묶는 과정이 빌드입니다. Build는 Node.js 환경에서 `Webpack`을 통해서 수행됩니다. 빌드는 **코드 난독화, 압축, 번들링** 등 여러 가지를 포함하는데, 그중 JavaScript 버전 문법 호환성은 `Babel`을 통해 해결합니다. 빌드 과정을 거친 결과물(`vendor.js`, **`index.html`**등)은 **nginx 폴더** 안에 저장이 됩니다. 이 결과물을 Nginx가 브라우저에 서빙하는 것입니다.
-- SPA (Single Page Application) +- **SPA (Single Page Application)**
Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이지 애플리케이션**으로, MPA와 대비됩니다. 비교를 위해 `MPA`를 설명하겠습니다. `MPA`는 Multi Page Application으로서, 페이지 이동 시마다 서버에 정적파일을 새로 요청하여 브라우저에 렌더링 하는 방식입니다. 반면 `SPA`는 최초 접속 시, 위의 빌드과정을 통해 사전에 만들어진 애플리케이션 구동에 필요한 모든 자원(`vendor.js`, `index.html` 등)을 한 번에 로드합니다. From 3098af79e521d024959713ff67ac9b86c6fabc82 Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 5 Feb 2026 19:29:27 +0900 Subject: [PATCH 10/33] update format in service-architecture --- .../infra/service-architecture/_index.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/content/developer/infra/service-architecture/_index.md b/docs/content/developer/infra/service-architecture/_index.md index 6995a69e6..1d98f6453 100644 --- a/docs/content/developer/infra/service-architecture/_index.md +++ b/docs/content/developer/infra/service-architecture/_index.md @@ -36,16 +36,16 @@ weight: 1 - **Build Process**
-Frontend 서버의 궁극적인 목표는 **정적파일 서빙**입니다. **배포 전 단계에서** 수행되는, 코드베이스에서 여러 가지로 흩어져 있는 소스코드를 하나로 묶는 과정이 빌드입니다. Build는 Node.js 환경에서 `Webpack`을 통해서 수행됩니다. 빌드는 **코드 난독화, 압축, 번들링** 등 여러 가지를 포함하는데, 그중 JavaScript 버전 문법 호환성은 `Babel`을 통해 해결합니다. +Frontend 서버의 궁극적인 목표는 **정적파일 서빙**입니다. **배포 전 단계에서** 수행되는, 코드베이스에서 여러 가지로 흩어져 있는 소스코드를 하나로 묶는 과정이 빌드입니다. Build는 Node.js 환경에서 `Webpack`을 통해서 수행됩니다. 빌드는 **코드 난독화, 압축, 번들링** 등 여러 가지를 포함하는데, 그중 JavaScript 버전 문법 호환성은 `Babel`을 통해 해결합니다.
빌드 과정을 거친 결과물(`vendor.js`, **`index.html`**등)은 **nginx 폴더** 안에 저장이 됩니다. 이 결과물을 Nginx가 브라우저에 서빙하는 것입니다.
- **SPA (Single Page Application)**
-Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이지 애플리케이션**으로, MPA와 대비됩니다. 비교를 위해 `MPA`를 설명하겠습니다. `MPA`는 Multi Page Application으로서, 페이지 이동 시마다 서버에 정적파일을 새로 요청하여 브라우저에 렌더링 하는 방식입니다. -반면 `SPA`는 최초 접속 시, 위의 빌드과정을 통해 사전에 만들어진 애플리케이션 구동에 필요한 모든 자원(`vendor.js`, `index.html` 등)을 한 번에 로드합니다. -이때 모든 정적파일을 모두 다 하나의 파일에 담지는 않습니다. 초기 로딩 속도 최적화를 위해 자주 쓰이지 않거나 무거운 기능은 **Chunk** 단위로 잘라두었다가, 실제 해당 기능이 필요할 때, 브라우저가 요청하여 로드하도록 설계되어 있습니다. +Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이지 애플리케이션**으로, MPA와 대비됩니다. 비교를 위해 `MPA`를 설명하겠습니다. `MPA`는 Multi Page Application으로서, 페이지 이동 시마다 서버에 정적파일을 새로 요청하여 브라우저에 렌더링 하는 방식입니다.
+반면 `SPA`는 최초 접속 시, 위의 빌드과정을 통해 사전에 만들어진 애플리케이션 구동에 필요한 모든 자원(`vendor.js`, `index.html` 등)을 한 번에 로드합니다.
+이때 모든 정적파일을 모두 다 하나의 파일에 담지는 않습니다. 초기 로딩 속도 최적화를 위해 자주 쓰이지 않거나 무거운 기능은 **Chunk** 단위로 잘라두었다가, 실제 해당 기능이 필요할 때, 브라우저가 요청하여 로드하도록 설계되어 있습니다.
이후 페이지 전환 시에는, 이미 서빙된 **index.html** 파일 위에서 사전에 로드된 혹은 필요 시 Chunk로 로드된 `vendor.js` 등의 자바스크립트가 컴포넌트만 교체하여 화면 전환을 수행합니다. 이를 통해 서버 요청을 최소화하여 MPA에 비해 빠른 사용성을 제공합니다. 이러한 빌드, SPA 방식을 통해서 화면 로드가 일어납니다. @@ -58,8 +58,8 @@ Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이 ### 3. Celery -`Celery`는 비동기 작업 큐 프레임워크로서, `Celery Beat`와 `Celery Worker`로 구성됩니다. -백엔드 로직 중 실시간성이 필요하지 않거나 리소스를 많이 점유하는 작업은 Redis를 사용하여 비동기로 처리합니다. 주로 *채점*, *이메일 발송*, *세션 관리*, 그리고 *등수/점수 업데이트와 같은 스케줄링 Job*이 여기에 해당합니다. +`Celery`는 비동기 작업 큐 프레임워크로서, `Celery Beat`와 `Celery Worker`로 구성됩니다.
+백엔드 로직 중 실시간성이 필요하지 않거나 리소스를 많이 점유하는 작업은 Redis를 사용하여 비동기로 처리합니다.
주로 *채점*, *이메일 발송*, *세션 관리*, 그리고 *등수/점수 업데이트와 같은 스케줄링 Job*이 여기에 해당합니다. 이 구조의 이해를 돕기 위해 Celery의 두 핵심 컴포넌트를 설명하겠습니다. - **Celery Worker :** Redis 큐에 쌓인 작업을 실제로 가져가서 수행하는 주체입니다. Django 서버와 실행환경은 유사하지만, Redis 큐에 있는 작업만 처리합니다. @@ -69,10 +69,10 @@ Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이 **[동작 프로세스 예시]** -1. **문제 제출 (`Celery Worker`)** +1. **문제 제출 (`Celery Worker`)**
사용자가 '제출하기' 버튼을 누르면, Django 서버는 직접 채점을 수행하지 않고 **채점 요청 메시지**를 `Redis Queue`에 등록만 하고 즉시 응답을 반환합니다. 대기 중이던 `Celery Worker`가 Redis Queue의 job을 감지하여 가져간 뒤 별도의 격리된 채점 서버와 통신하며 채점을 진행합니다. -2. **통계 업데이트 (`Celery Beat`)** +2. **통계 업데이트 (`Celery Beat`)**
사용자가 직접 요청하지 않아도 시스템이 수행해야 하는 '랭킹 업데이트' 같은 작업은 `Celery Beat`가 담당합니다. `Celery Beat`는 설정된 시각이 되면 자동으로 Redis에 작업 요청을 넣고, `Celery Worker`가 이를 가져가서 점수를 갱신합니다. `Celery Worker`는 Django 서버와 본질적으로 유사한 환경(동일한 코드베이스, ORM 사용)을 가집니다.  다만 가장 큰 차이점은 수신 대상이 다르다는 것입니다. From a7890e1c70660872d8f8cef833bba9724e67a2bf Mon Sep 17 00:00:00 2001 From: Minchan Kim Date: Thu, 5 Feb 2026 19:32:43 +0900 Subject: [PATCH 11/33] update format in service-architecture --- docs/content/developer/infra/service-architecture/_index.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/content/developer/infra/service-architecture/_index.md b/docs/content/developer/infra/service-architecture/_index.md index 1d98f6453..3d679dc79 100644 --- a/docs/content/developer/infra/service-architecture/_index.md +++ b/docs/content/developer/infra/service-architecture/_index.md @@ -36,8 +36,8 @@ weight: 1 - **Build Process**
-Frontend 서버의 궁극적인 목표는 **정적파일 서빙**입니다. **배포 전 단계에서** 수행되는, 코드베이스에서 여러 가지로 흩어져 있는 소스코드를 하나로 묶는 과정이 빌드입니다. Build는 Node.js 환경에서 `Webpack`을 통해서 수행됩니다. 빌드는 **코드 난독화, 압축, 번들링** 등 여러 가지를 포함하는데, 그중 JavaScript 버전 문법 호환성은 `Babel`을 통해 해결합니다.
-빌드 과정을 거친 결과물(`vendor.js`, **`index.html`**등)은 **nginx 폴더** 안에 저장이 됩니다. 이 결과물을 Nginx가 브라우저에 서빙하는 것입니다. +Frontend 서버의 궁극적인 목표는 **정적파일 서빙**입니다. **배포 전 단계에서** 수행되는, 코드베이스에서 여러 가지로 흩어져 있는 소스코드를 하나로 묶는 과정이 빌드입니다. Build는 Node.js 환경에서 `Webpack`을 통해서 수행됩니다.
빌드는 **코드 난독화, 압축, 번들링** 등 여러 가지를 포함하는데, 그중 JavaScript 버전 문법 호환성은 `Babel`을 통해 해결합니다.
+빌드 과정을 거친 결과물(`vendor.js`, `index.html`등)은 **nginx 폴더** 안에 저장이 됩니다. 이 결과물을 Nginx가 브라우저에 서빙하는 것입니다.
@@ -82,7 +82,7 @@ Code Place는 `SPA`방식을 사용하고 있습니다. `SPA`는 **단일 페이
-**왜 이렇게 나누었을까?** +**왜 이렇게 나누었을까요?** 만약 Celery 없이 Django가 모든 작업을 직접 처리한다고 가정해봅시다. From d656aafd383b7951e0a8d162f4090cbefa194c0f Mon Sep 17 00:00:00 2001 From: Junwoo Park <64635737+Boksam@users.noreply.github.com> Date: Mon, 9 Feb 2026 16:33:55 +0900 Subject: [PATCH 12/33] =?UTF-8?q?#566=20Grafana=20=EB=8C=80=EC=8B=9C?= =?UTF-8?q?=EB=B3=B4=EB=93=9C=20=EA=B5=AC=EC=B6=95=20(#567)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * setup grafana * resize pvc from 5gb to 1gb * modify comments --- kubernetes/base/kustomization.yaml | 3 ++ kubernetes/base/monitoring/grafana/README.md | 48 +++++++++++++++++++ .../base/monitoring/grafana/values.yaml | 41 ++++++++++++++++ kubernetes/base/monitoring/namespace.yaml | 5 ++ kubernetes/overlays/dev/grafana-values.yaml | 10 ++++ 5 files changed, 107 insertions(+) create mode 100644 kubernetes/base/monitoring/grafana/README.md create mode 100644 kubernetes/base/monitoring/grafana/values.yaml create mode 100644 kubernetes/base/monitoring/namespace.yaml create mode 100644 kubernetes/overlays/dev/grafana-values.yaml diff --git a/kubernetes/base/kustomization.yaml b/kubernetes/base/kustomization.yaml index 1cc46d33b..7f5e84d4f 100644 --- a/kubernetes/base/kustomization.yaml +++ b/kubernetes/base/kustomization.yaml @@ -3,6 +3,9 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization resources: + # Monitoring + - ./monitoring/namespace.yaml + # Persistent Volume Claims - ./backend/pvc.yaml diff --git a/kubernetes/base/monitoring/grafana/README.md b/kubernetes/base/monitoring/grafana/README.md new file mode 100644 index 000000000..34990c3db --- /dev/null +++ b/kubernetes/base/monitoring/grafana/README.md @@ -0,0 +1,48 @@ +# Grafana (Helm) 배포 가이드 + +이 디렉토리는 Grafana Helm Chart의 설정을 환경별로 관리합니다. + +- `base/monitoring/grafana/values.yaml`: 모든 환경에 적용되는 공통 설정 파일입니다. +- `overlays/<환경>/grafana-values.yaml`: 각 환경에만 적용되는 특별 설정 파일입니다. (예: Ingress 호스트 주소) + +## 사전 조건 + +- `helm` CLI가 설치되어 있어야 합니다. +- `kubectl`이 클러스터에 연결되어 있어야 합니다. +- Grafana Helm 리포지토리가 추가되어 있어야 합니다. + +```sh +# 리포지토리 추가 (최초 1회) +helm repo add grafana https://grafana-community.github.io/helm-charts +helm repo update +``` + +## 환경별 배포 명령어 + +`helm upgrade --install` 명령어를 사용하여 Grafana를 설치하거나 업그레이드합니다. +`-f` 옵션을 사용하여 **공통 `values.yaml`을 먼저 지정하고, 그 다음 환경별 `values.yaml`을 지정**하여 설정을 덮어씁니다. + +### Dev 환경 배포 + +```sh +# 프로젝트 루트 디렉토리에서 실행 + +helm upgrade --install grafana grafana/grafana \ + --namespace monitoring \ + --create-namespace \ + -f kubernetes/base/monitoring/grafana/values.yaml \ + -f kubernetes/overlays/dev/grafana-values.yaml +``` + +### Prod 환경 배포 + +```sh +# 프로젝트 루트 디렉토리에서 실행 + +helm upgrade --install grafana grafana/grafana \ + --namespace monitoring \ + --create-namespace \ + -f kubernetes/base/monitoring/grafana/values.yaml \ + -f kubernetes/overlays/prod/grafana-values.yaml +``` + diff --git a/kubernetes/base/monitoring/grafana/values.yaml b/kubernetes/base/monitoring/grafana/values.yaml new file mode 100644 index 000000000..9cc4a4e15 --- /dev/null +++ b/kubernetes/base/monitoring/grafana/values.yaml @@ -0,0 +1,41 @@ +# ======================================================================== +# NOTE: Grafana Helm Chart 공통 설정 (base) +# https://github.com/grafana-community/helm-charts/blob/main/charts/grafana/values.yaml +# ======================================================================== + +# 환경과 관계없이 공통으로 적용되는 설정을 정의합니다. +# 환경별로 달라져야 하는 값(예: ingress.hosts)은 여기서 정의하지 않거나, +# overlays의 values 파일에서 덮어쓸 수 있도록 기본값만 정의합니다. + +# 데이터 영속성 설정 +persistence: + type: pvc + enabled: true + storageClassName: longhorn + accessModes: + - ReadWriteOnce + size: 1Gi + +# Ingress 설정 (기본적으로 비활성화, 각 overlay에서 활성화 및 host 설정) +ingress: + enabled: true + ingressClassName: traefik + annotations: + traefik.ingress.kubernetes.io/router.tls.certresolver: default + # hosts, tls 설정은 각 overlay의 values.yaml 파일에서 정의합니다. + hosts: [] + # - grafana.example.com + tls: [] + # - secretName: grafana-tls + # hosts: + # - grafana.example.com +# 기본 데이터소스 및 대시보드 설정 +# datasources: +# datasources.yaml: +# apiVersion: 1 +# datasources: +# - name: Prometheus +# type: prometheus +# url: http://prometheus-server.prometheus.svc.cluster.local +# access: proxy +# isDefault: true diff --git a/kubernetes/base/monitoring/namespace.yaml b/kubernetes/base/monitoring/namespace.yaml new file mode 100644 index 000000000..9aa9a213b --- /dev/null +++ b/kubernetes/base/monitoring/namespace.yaml @@ -0,0 +1,5 @@ +# kubernetes/base/monitoring/namespace.yaml +apiVersion: v1 +kind: Namespace +metadata: + name: monitoring diff --git a/kubernetes/overlays/dev/grafana-values.yaml b/kubernetes/overlays/dev/grafana-values.yaml new file mode 100644 index 000000000..bdebde05c --- /dev/null +++ b/kubernetes/overlays/dev/grafana-values.yaml @@ -0,0 +1,10 @@ +# kubernetes/overlays/dev/grafana-values.yaml +# dev 환경용 Grafana values + +ingress: + enabled: true + hosts: + - monitoring.code-place-dev.site + tls: + - hosts: + - monitoring.code-place-dev.site From 037ac3a4cbe1d68fb46ea6b696686e10acb72ce6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=AC=B8=EC=A7=84=ED=98=81?= <87259233+wlsgur11@users.noreply.github.com> Date: Tue, 10 Feb 2026 15:49:39 +0900 Subject: [PATCH 13/33] =?UTF-8?q?#569=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=EB=8D=94=EB=B3=B4=EA=B8=B0=20=EA=B3=A0=EC=A0=95=20?= =?UTF-8?q?=EA=B2=8C=EC=8B=9C=EB=AC=BC=20=EB=94=94=EC=9E=90=EC=9D=B8=20?= =?UTF-8?q?=EC=A0=81=EC=9A=A9=20(#570)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * 공지사항 더보기 고정 게시물 색깔, 호버 효과, 이모지 추가 * 공지사항 박스 크기 조절 * prettier format 적용 --- .../oj/views/home/Notice/HomeNoticeBox.vue | 2 +- .../oj/views/notice/AnnouncementItem.vue | 21 +++++++++++++++++-- 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/frontend/src/pages/oj/views/home/Notice/HomeNoticeBox.vue b/frontend/src/pages/oj/views/home/Notice/HomeNoticeBox.vue index bd8f25e6b..3415093cb 100644 --- a/frontend/src/pages/oj/views/home/Notice/HomeNoticeBox.vue +++ b/frontend/src/pages/oj/views/home/Notice/HomeNoticeBox.vue @@ -139,7 +139,7 @@ export default { border-radius: 7px; border: 1px solid #dedede; width: 100%; - height: 457px; + height: auto; padding: 0 30px; margin-bottom: 20px; transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1); diff --git a/frontend/src/pages/oj/views/notice/AnnouncementItem.vue b/frontend/src/pages/oj/views/notice/AnnouncementItem.vue index 1eb8f4889..9a53fb349 100644 --- a/frontend/src/pages/oj/views/notice/AnnouncementItem.vue +++ b/frontend/src/pages/oj/views/notice/AnnouncementItem.vue @@ -48,17 +48,33 @@ export default { From 3c81fd95d5bab56e230ca5f66125450323eafd1a Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 10 Feb 2026 06:52:00 +0000 Subject: [PATCH 14/33] ci: Update dev image tags to 037ac3a4cbe1d68fb46ea6b696686e10acb72ce6 [skip ci] --- kubernetes/overlays/dev/kustomization.yaml | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/kubernetes/overlays/dev/kustomization.yaml b/kubernetes/overlays/dev/kustomization.yaml index d96105849..b310d1f98 100644 --- a/kubernetes/overlays/dev/kustomization.yaml +++ b/kubernetes/overlays/dev/kustomization.yaml @@ -5,9 +5,7 @@ resources: - ./secrets/pg-credentials.yaml - ./secrets/regcred.yaml - ./secrets/common-credentials.yaml - namespace: code-place-dev - patches: - path: backend-deployment-patch.yaml - path: frontend-deployment-patch.yaml @@ -15,7 +13,6 @@ patches: - path: celery-worker-deployment-patch.yaml - path: celery-beat-deployment-patch.yaml - path: judge-server-deployment-patch.yaml - # NOTE: base 디렉토리의 이미지 이름과 태그를 오버라이드합니다. # `backend`, `frontend` 이미지 태그는 CI 파이프라인에서 자동으로 변경됩니다. # `judge-server` 이미지는 자주 변경되지 않으므로 수동으로 설정합니다. @@ -25,7 +22,7 @@ images: newTag: eb40822b5b950bf75f00ed053609724a2a10be7f-dev - name: frontend newName: harbor.code-place-dev.site/code-place-dev/frontend - newTag: 61af4b605d731c1ec378f124b0e9a44c0438d35b-dev + newTag: 037ac3a4cbe1d68fb46ea6b696686e10acb72ce6-dev - name: judge-server newName: harbor.code-place-dev.site/code-place-dev/judge-server newTag: 1.0.3 From caaf8cc596132e355ce3f05350e13fca751b50d5 Mon Sep 17 00:00:00 2001 From: tae Date: Wed, 11 Feb 2026 15:35:01 +0900 Subject: [PATCH 15/33] =?UTF-8?q?fix:=20=EA=B3=B5=EC=A7=80=EC=82=AC?= =?UTF-8?q?=ED=95=AD=20=ED=85=8D=EC=8A=A4=ED=8A=B8=20=ED=81=B4=EB=A6=AD=20?= =?UTF-8?q?=EC=8B=9C=20=EB=AA=A9=EB=A1=9D=EC=9C=BC=EB=A1=9C=20=EB=9D=BC?= =?UTF-8?q?=EC=9A=B0=ED=8C=85=EB=90=98=EB=8A=94=20=EB=AC=B8=EC=A0=9C=20?= =?UTF-8?q?=ED=95=B4=EA=B2=B0=20(#572)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../oj/views/home/Notice/HomeNoticeBox.vue | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/frontend/src/pages/oj/views/home/Notice/HomeNoticeBox.vue b/frontend/src/pages/oj/views/home/Notice/HomeNoticeBox.vue index 3415093cb..a23403ce0 100644 --- a/frontend/src/pages/oj/views/home/Notice/HomeNoticeBox.vue +++ b/frontend/src/pages/oj/views/home/Notice/HomeNoticeBox.vue @@ -1,7 +1,7 @@