๐ŸฆŠ Weasel ํ”„๋กœ์ ํŠธ โ€“ AI ๊ธฐ๋ฐ˜ ๋ฌธ์ œํ’€์ด ์„œ๋น„์Šค

Sb_chiยท2024๋…„ 9์›” 25์ผ

project

๋ชฉ๋ก ๋ณด๊ธฐ
2/2
post-thumbnail

๐Ÿ“Œ Intro

Weasel ํ”„๋กœ์ ํŠธ๋Š” AWS Bedrock Claude Sonnet 3.5 ๋ชจ๋ธ์„ ํ™œ์šฉํ•œ ๋ฌธ์ œํ’€์ด ์„œ๋น„์Šค์ž…๋‹ˆ๋‹ค.
์‚ฌ์šฉ์ž๊ฐ€ ๋ฌธ์ œ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๋ฉด AI๊ฐ€ ์ •๋‹ต๊ณผ ํ•ด์„ค์„ ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค.
์ด 6๋ช… (Frontend 2๋ช…, Backend 2๋ช…, Infra 2๋ช…)์ด ์ฐธ์—ฌํ–ˆ๊ณ , ์ €๋Š” ์ธํ”„๋ผ ํŒ€์˜ ์ผ์›์œผ๋กœ ํ”„๋กœ์ ํŠธ๋ฅผ ์„ฑ๊ณต์ ์œผ๋กœ ๋งˆ๋ฌด๋ฆฌํ–ˆ์Šต๋‹ˆ๋‹ค.


๋Œ€ํ‘œ์ด๋ฏธ์ง€


๐Ÿ“ ํ”„๋กœ์ ํŠธ ๊ฐœ์š”

  • ๋ชฉํ‘œ: ๋ฌธ์ œ ์ด๋ฏธ์ง€ ์—…๋กœ๋“œ โ†’ AI ์ •๋‹ตยทํ•ด์„ค ์ œ๊ณต + CI/CD + ๋ชจ๋‹ˆํ„ฐ๋ง ํ™˜๊ฒฝ ๊ตฌ์„ฑ
  • ์ฃผ์š” ์Šคํƒ:
    • Backend: Spring Boot
    • Frontend: React
    • IaC(์ธํ”„๋ผ ์ฝ”๋“œํ™”): Terraform
    • ๋ฐฐํฌ ํ™˜๊ฒฝ: Amazon EKS, S3, Route53, CloudFront
    • AWS ์„œ๋น„์Šค: VPC, ECR, Secrets Manager, WAF
    • CI/CD: Jenkins, ArgoCD
    • GenAI ๋ชจ๋ธ: AWS Bedrock (Claude Sonnet 3.5)
    • ๋ชจ๋‹ˆํ„ฐ๋ง: Prometheus, Grafana

๐Ÿ— ์•„ํ‚คํ…์ฒ˜

  • AI ์ฒ˜๋ฆฌ ํ๋ฆ„: ๋ฌธ์ œ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•˜๋ฉด API๊ฐ€ AWS Bedrock(Claude Sonnet 3.5)๊ณผ ์—ฐ๋™๋˜์–ด, ๋น ๋ฅด๊ณ  ์ •ํ™•ํ•œ ์ •๋‹ตยทํ•ด์„ค์„ ๋ฐ˜ํ™˜ํ•˜๋„๋ก ๊ตฌ์„ฑ
  • CI/CD ์ž๋™ํ™”: ์ฝ”๋“œ ๋ณ€๊ฒฝ ์‹œ Jenkins๊ฐ€ ์ž๋™ ๋นŒ๋“œ๋ฅผ ์ˆ˜ํ–‰ํ•˜๊ณ , ArgoCD๋กœ EKS์— ๋ฐฐํฌํ•˜์—ฌ ๋ฐฐํฌ ์†๋„์™€ ์•ˆ์ •์„ฑ์„ ๋†’์ž„
  • ๋ชจ๋‹ˆํ„ฐ๋ง: Prometheus์™€ Grafana๋กœ CPUยท๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰, API ์‘๋‹ต ์†๋„, ์—๋Ÿฌ์œจ์„ ์‹ค์‹œ๊ฐ„ ํ™•์ธํ•ด ์žฅ์• ๋ฅผ ์กฐ๊ธฐ ๋ฐœ๊ฒฌ
  • ๋ณด์•ˆ ๋ฐ HTTPS ์ ์šฉ: CloudFront์™€ WAF๋ฅผ ํ™œ์šฉํ•ด ์ „ ๊ตฌ๊ฐ„ HTTPS ํ†ต์‹ ๊ณผ ์›น ๊ณต๊ฒฉ ๋ฐฉ์–ด๋ฅผ ๊ตฌํ˜„
  • ์ธํ”„๋ผ ์ฝ”๋“œํ™”: Terraform์œผ๋กœ ์ธํ”„๋ผ๋ฅผ ์ฝ”๋“œ๋กœ ๊ด€๋ฆฌํ•ด, ํ™˜๊ฒฝ ์žฌํ˜„์„ฑ๊ณผ ์žฌ์‚ฌ์šฉ์„ฑ์„ ํ™•๋ณด

โš™๏ธ CI/CD ์•„ํ‚คํ…์ฒ˜

๋ฐฑ์—”๋“œ ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ (Spring Boot โ†’ EKS)

  1. GitHub์— ์ฝ”๋“œ๋ฅผ ์˜ฌ๋ฆฌ๋ฉด Jenkins๊ฐ€ ์ž๋™์œผ๋กœ ๋นŒ๋“œ๋ฅผ ์‹œ์ž‘ํ•ฉ๋‹ˆ๋‹ค.
  2. ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋นŒ๋“œํ•˜๊ณ , Docker ์ด๋ฏธ์ง€๋ฅผ ๋งŒ๋“ค์–ด ECR์— ์ €์žฅํ•ฉ๋‹ˆ๋‹ค.
  3. ArgoCD๊ฐ€ ๋งค๋‹ˆํŽ˜์ŠคํŠธ(Helm/Kustomize) ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ๊ฐ์ง€ํ•ฉ๋‹ˆ๋‹ค.
  4. ๋ณ€๊ฒฝ๋œ ๋‚ด์šฉ์„ EKS์— ๋ฐ˜์˜ํ•˜๋ฉฐ, ๋กค๋ง ์—…๋ฐ์ดํŠธ ๋˜๋Š” Blue/Green ๋ฐฉ์‹์œผ๋กœ ๋ฐฐํฌํ•ฉ๋‹ˆ๋‹ค.

ํ”„๋ก ํŠธ์—”๋“œ ๋ฐฐํฌ ํŒŒ์ดํ”„๋ผ์ธ (React โ†’ S3ยทCloudFront)

  1. GitHub์— ์ฝ”๋“œ๋ฅผ ์˜ฌ๋ฆฌ๋ฉด Jenkins๊ฐ€ ํ”„๋ก ํŠธ์—”๋“œ ๋นŒ๋“œ๋ฅผ ์‹คํ–‰ํ•ฉ๋‹ˆ๋‹ค.
  2. ๋นŒ๋“œ๋œ ์ •์  ํŒŒ์ผ(build/)์„ S3 ๋ฒ„ํ‚ท์— ์—…๋กœ๋“œํ•ฉ๋‹ˆ๋‹ค.
  3. ๋ฐฐํฌ ์‹œ CloudFront ์บ์‹œ๋ฅผ ์ž๋™ ๋ฌดํšจํ™”๋กœ ์ตœ์‹  ํŒŒ์ผ์„ ๋ฐ›์„ ์ˆ˜ ์žˆ๊ฒŒ ๊ตฌ์„ฑํ–ˆ์Šต๋‹ˆ๋‹ค.


Jenkins ๋นŒ๋“œ ํŒŒ์ดํ”„๋ผ์ธ ์‹คํ–‰ ํ™”๋ฉด


๐Ÿ“ข ๋ฐฐํฌ ์•Œ๋ฆผ ์ž๋™ํ™”

  • Jenkins ๋นŒ๋“œ ์™„๋ฃŒ ํ›„ Slack Webhook์„ ํ†ตํ•ด ์„ฑ๊ณต/์‹คํŒจ ์—ฌ๋ถ€ ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ
  • ๋ฐฑ์—”๋“œยทํ”„๋ก ํŠธ์—”๋“œ ๊ฐ๊ฐ ๋ณ„๋„ ํŒŒ์ดํ”„๋ผ์ธ์œผ๋กœ ๋นŒ๋“œ ์ƒํƒœ ํ™•์ธ ๊ฐ€๋Šฅ
  • ์•Œ๋ฆผ ๋ฉ”์‹œ์ง€์— Jenkins Job URL์„ ํฌํ•จํ•ด ์ฆ‰์‹œ ๋กœ๊ทธยท์ƒํƒœ ํ™•์ธ ๊ฐ€๋Šฅ

slack-jenkins์•Œ๋ฆผ
Jenkins ๋นŒ๋“œ ์„ฑ๊ณต ์‹œ Slack ์ฑ„๋„์— ์‹ค์‹œ๊ฐ„ ์•Œ๋ฆผ


๐Ÿ’ป ์„œ๋น„์Šค ํ™”๋ฉด

๋ฌธ์ œ ์—…๋กœ๋“œ ํ™”๋ฉด
๋ฌธ์ œ ์ด๋ฏธ์ง€๋ฅผ ์—…๋กœ๋“œํ•  ์ˆ˜ ์žˆ๋Š” ์ž…๋ ฅ UI ๋ฐ ์ฑ„ํŒ… ๋‚ด์šฉ ์ €์žฅ

AI ์‘๋‹ต ํ™”๋ฉด
์—…๋กœ๋“œํ•œ ๋ฌธ์ œ ์ด๋ฏธ์ง€์— ๋Œ€ํ•ด AI๊ฐ€ ์ •๋‹ต๊ณผ ํ•ด์„ค์„ ๋ฐ˜ํ™˜ํ•˜๋Š” ํ™”๋ฉด


๐Ÿ“Š ๋ชจ๋‹ˆํ„ฐ๋ง


Grafana ๋Œ€์‹œ๋ณด๋“œ

Prometheus์™€ Grafana๋ฅผ ํ™œ์šฉํ•ด CPUยท๋ฉ”๋ชจ๋ฆฌ ์‚ฌ์šฉ๋Ÿ‰, API ์‘๋‹ต ์†๋„, ์—๋Ÿฌ์œจ ๋“ฑ์„ ์‹ค์‹œ๊ฐ„์œผ๋กœ ๋ชจ๋‹ˆํ„ฐ๋งํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด๋ฅผ ํ†ตํ•ด ์„ฑ๋Šฅ ์ €ํ•˜๋‚˜ ์žฅ์•  ์ง•ํ›„๋ฅผ ์กฐ๊ธฐ์— ๋ฐœ๊ฒฌํ•˜๊ณ  ์‹ ์†ํžˆ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.


โš ๏ธ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… : Node.js Dockerfile ์ตœ์ ํ™”

๋ฌธ์ œ

  • Dockerfile์ด ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ํ”„๋กœ๋•์…˜ ํ™˜๊ฒฝ ๊ตฌ๋ถ„ ์—†์ด ์ž‘์„ฑ๋˜์–ด ์ตœ์ข… ์ด๋ฏธ์ง€์— ๋ถˆํ•„์š”ํ•œ ๊ฐœ๋ฐœ ๋„๊ตฌ์™€ ์˜์กด์„ฑ์ด ํฌํ•จ๋จ
  • ๋นŒ๋“œ ๊ณผ์ • ์—†์ด ๋ฐ”๋กœ ์‹คํ–‰ํ•ด ์ตœ์ ํ™”๊ฐ€ ์ „ํ˜€ ์ด๋ฃจ์–ด์ง€์ง€ ์•Š์Œ
  • ์‹คํ–‰ ๋ช…๋ น์–ด(npm run dev)๊ฐ€ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ๊ธฐ์ค€์œผ๋กœ ์„ค์ •๋˜์–ด ํ”„๋กœ๋•์…˜์— ์ ํ•ฉํ•˜์ง€ ์•Š์Œ

์›์ธ

  1. ๋ฒ ์ด์Šค ์ด๋ฏธ์ง€
    • node:20.14.0 ์ด๋ฏธ์ง€๋Š” ๊ฐœ๋ฐœ ๋„๊ตฌ๊ฐ€ ํฌํ•จ๋˜์–ด ์ตœ์ข… ์ด๋ฏธ์ง€ ํฌ๊ธฐ๊ฐ€ ์ปค์ง
  2. ๋นŒ๋“œ ๊ณผ์ • ๋ˆ„๋ฝ
    • ์ตœ์ ํ™”๋œ ๋นŒ๋“œ ์‚ฐ์ถœ๋ฌผ์ด ์•„๋‹Œ ์›๋ณธ ์†Œ์Šค ์ฝ”๋“œ๋กœ ์‹คํ–‰
  3. ์‹คํ–‰ ๋ช…๋ น์–ด ๋ถ€์ ์ ˆ
    • npm run dev โ†’ ๊ฐœ๋ฐœ ์˜์กด์„ฑ(devDependencies)๊นŒ์ง€ ํฌํ•จ ์„ค์น˜
    • ํ”„๋กœ๋•์…˜์—์„œ๋Š” npm start ์‚ฌ์šฉ์ด ์ ํ•ฉ

ํ•ด๊ฒฐ

  • ์ด๋ฏธ์ง€ ์ตœ์ ํ™”: ๋นŒ๋“œ ๋‹จ๊ณ„์™€ ๋ฐฐํฌ ๋‹จ๊ณ„๋ฅผ ๋ถ„๋ฆฌ (multi-stage build)
  • ์Šฌ๋ฆผ ์ด๋ฏธ์ง€ ์‚ฌ์šฉ: ํ”„๋กœ๋•์…˜ ๋‹จ๊ณ„์—์„œ node:<๋ฒ„์ „>-slim ์‚ฌ์šฉ
  • ๋นŒ๋“œ ๋ช…๋ น์–ด ์ถ”๊ฐ€: RUN yarn build๋กœ ํ”„๋กœ๋•์…˜ ๋นŒ๋“œ ์ƒ์„ฑ
  • ์‹คํ–‰ ๋ช…๋ น์–ด ๋ณ€๊ฒฝ: CMD ["npm", "start"]๋กœ ํ”„๋กœ๋•์…˜ ์‹คํ–‰

๋ณ€๊ฒฝ ์ „/ํ›„ ๋น„๊ต

๋ณ€๊ฒฝ ์ „

FROM node:20.14.0

WORKDIR /usr/src/app

COPY package.json yarn.lock ./
RUN yarn install

COPY . .

EXPOSE 3000
CMD ["npm", "run", "dev"]

๋ณ€๊ฒฝ ํ›„ (Multi-stage build)

# ๋นŒ๋“œ ๋‹จ๊ณ„
FROM node:20.14.0 AS builder
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
RUN yarn install --frozen-lockfile
COPY . .
RUN yarn build

# ํ”„๋กœ๋•์…˜ ๋‹จ๊ณ„
FROM node:20.14.0-slim
WORKDIR /usr/src/app
COPY package.json yarn.lock ./
RUN yarn install --production --frozen-lockfile
COPY --from=builder /usr/src/app/dist ./dist

EXPOSE 3000
CMD ["npm", "start"]

ํšจ๊ณผ

  • ์ด๋ฏธ์ง€ ํฌ๊ธฐ ๊ฐ์†Œ: ๋ถˆํ•„์š”ํ•œ ๊ฐœ๋ฐœ ๋„๊ตฌ/์˜์กด์„ฑ ์ œ๊ฑฐ
  • ์‹คํ–‰ ํšจ์œจ ํ–ฅ์ƒ: ์ตœ์ ํ™”๋œ ๋นŒ๋“œ ๊ฒฐ๊ณผ๋ฌผ๋งŒ ๋ฐฐํฌ
  • ํ™˜๊ฒฝ ๋ถ„๋ฆฌ: ๊ฐœ๋ฐœ/๋ฐฐํฌ ํ™˜๊ฒฝ ๋ช…ํ™•ํ•˜๊ฒŒ ๊ตฌ๋ถ„

โš ๏ธ ํŠธ๋Ÿฌ๋ธ”์ŠˆํŒ… : Jenkins ํŒŒ์ดํ”„๋ผ์ธ ๋ธŒ๋žœ์น˜ ํ•„ํ„ฐ๋ง

๋ฌธ์ œ

  • ๋ฉ”์ธ ๋ธŒ๋žœ์น˜์— ๋ณ‘ํ•ฉํ•˜๊ธฐ ์ „์— ์ƒ์„ฑ๋œ ๋ธŒ๋žœ์น˜์—์„œ push ์‹œ์—๋„ Jenkins ํŒŒ์ดํ”„๋ผ์ธ์ด ์‹คํ–‰๋จ
  • main ๋ธŒ๋žœ์น˜ ๋ณ€๊ฒฝ ์‹œ์—๋งŒ ํŒŒ์ดํ”„๋ผ์ธ์ด ๋™์ž‘ํ•˜๋„๋ก ์ œ์–ด ํ•„์š”

์›์ธ

  • ๊ธฐ์กด ์„ค์ •(GitHub hook trigger for GITScm polling)์€ ๋ชจ๋“  ๋ธŒ๋žœ์น˜ ๋ณ€๊ฒฝ ์‹œ ํŒŒ์ดํ”„๋ผ์ธ์ด ์‹คํ–‰๋จ
  • ๋ธŒ๋žœ์น˜ ์กฐ๊ฑด ํ•„ํ„ฐ๋ง ๋กœ์ง์ด ์—†์–ด main ์™ธ ๋ธŒ๋žœ์น˜์—์„œ๋„ ๋นŒ๋“œ๊ฐ€ ์ˆ˜ํ–‰๋จ

ํ•ด๊ฒฐ

1. Plugin ๋ณ€๊ฒฝ

  • GitHub hook trigger for GITScm polling โ†’ Generic Webhook Trigger


2. Build Triggers ์„ค์ •

  • ๊ธฐ์กด: GitHub hook trigger for GITScm polling
  • ๋ณ€๊ฒฝ: Generic Webhook Trigger

3. Post Content Parameters

  1. genericVariables
    • key: 'ref', value: '$.ref' ์„ค์ •
    • JSONPath($.ref)๋กœ ์›นํ›… ํŽ˜์ด๋กœ๋“œ์—์„œ ๋ธŒ๋žœ์น˜ ์ •๋ณด ์ถ”์ถœ ํ›„ ๋ณ€์ˆ˜ ref์— ์ €์žฅ
  2. regexpFilterText
    • $ref๋กœ ์„ค์ • โ†’ ์ถ”์ถœ๋œ ๋ธŒ๋žœ์น˜ ์ •๋ณด(ref)๋ฅผ ํ•„ํ„ฐ ํ…์ŠคํŠธ๋กœ ์‚ฌ์šฉ
  3. regexpFilterExpression
    • 'refs/heads/main' ์ง€์ • โ†’ main ๋ธŒ๋žœ์น˜์ผ ๋•Œ๋งŒ ๋นŒ๋“œ ์‹คํ–‰


4. Optional filter

  • Text: ref ๋ณ€์ˆ˜ ๊ฐ’
  • Expression: refs/heads/main


์›นํ›… ์„ค์ •

  • Payload URL
    http://<๋„๋ฉ”์ธ ๋˜๋Š” IP>/generic-webhook-trigger/invoke?token=<ํ† ํฐ>
  • Content type: application/json

๋ณ€๊ฒฝ ์ „/ํ›„ ๋น„๊ต

ํ•ญ๋ชฉ๋ณ€๊ฒฝ ์ „๋ณ€๊ฒฝ ํ›„
Build TriggerGitHub hook trigger for GITScm pollingGeneric Webhook Trigger
๋ธŒ๋žœ์น˜ ํ•„ํ„ฐ๋ง๋ชจ๋“  ๋ธŒ๋žœ์น˜์—์„œ ๋นŒ๋“œ ์‹คํ–‰main ๋ธŒ๋žœ์น˜์—์„œ๋งŒ ๋นŒ๋“œ ์‹คํ–‰
ํ•„ํ„ฐ ์กฐ๊ฑด์—†์ŒJSONPath($.ref)๋กœ ๋ธŒ๋žœ์น˜ ์ถ”์ถœ โ†’ ์ •๊ทœ์‹(refs/heads/main) ๋งค์นญ

๐Ÿ’ก ๋งˆ๋ฌด๋ฆฌ

Weasel ํ”„๋กœ์ ํŠธ๋ฅผ ํ†ตํ•ด ํŒ€์› ๊ฐ„ ์†Œํ†ต๊ณผ ํ˜‘๋ ฅ์˜ ์ค‘์š”์„ฑ์„ ๊นŠ์ด ๋А๊ผˆ์Šต๋‹ˆ๋‹ค.
์ฒซ ํŒ€ ํ”„๋กœ์ ํŠธ์˜€๋˜ ๋งŒํผ ๋ถ€์กฑํ•œ ์ ๋„ ๋งŽ์•˜์ง€๋งŒ, ๊ฐœ์ธ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ๋“œ๋Ÿฌ๋‚˜์ง€ ์•Š์•˜๋˜ ๋ณด์•ˆ ๊ด€๋ฆฌ, ์šด์˜ ์•ˆ์ •์„ฑ, ๋ฐฐํฌ ํšจ์œจ์„ฑ์˜ ํ•œ๊ณ„๋ฅผ ์ฒด๊ฐํ–ˆ์Šต๋‹ˆ๋‹ค.
์ด๋ฅผ ํ•ด๊ฒฐํ•˜๊ธฐ ์œ„ํ•ด CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ์ตœ์ ํ™”ํ•ด ๋ฐฐํฌ ์†๋„๋ฅผ ๊ฐœ์„ ํ•˜๊ณ , GrafanaยทPrometheus ๊ธฐ๋ฐ˜ ๋ชจ๋‹ˆํ„ฐ๋ง ํ™˜๊ฒฝ์„ ๊ตฌ์ถ•ํ•ด ์žฅ์•  ๋ฐœ์ƒ ์‹œ ์‹ ์†ํ•˜๊ฒŒ ๋Œ€์‘ํ•  ์ˆ˜ ์žˆ๋Š” ์ฒด๊ณ„๋ฅผ ๋งˆ๋ จํ•˜๋Š” ๋“ฑ ๋งŽ์€ ๊ฒƒ์„ ๋ฐฐ์› ์Šต๋‹ˆ๋‹ค.

๋ถ€์กฑํ•˜๋‹ค ๋А๋‚€์ 

ํ–ฅํ›„์—๋Š” ๋น„์šฉ ํšจ์œจ์„ฑ์„ ์œ„ํ•ด Jenkins Fleet์„ ๋„์ž…ํ•ด, ๋นŒ๋“œ ์‹œ์—๋งŒ ์—์ด์ „ํŠธ๋ฅผ ๋™์ ์œผ๋กœ ์ƒ์„ฑํ•˜๊ณ  ์‚ฌ์šฉ ํ›„ ์ž๋™ ์ข…๋ฃŒํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ EC2 ๋น„์šฉ์„ ์ ˆ๊ฐํ•˜๋Š” ๋ฐฉ์•ˆ๋„ ๊ณ ๋ คํ•˜๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค.
์•ž์œผ๋กœ๋„ IaC ๊ธฐ๋ฐ˜ ํด๋ผ์šฐ๋“œ ์•„ํ‚คํ…์ฒ˜ ์„ค๊ณ„ ์—ญ๋Ÿ‰์„ ํ™•์žฅํ•ด, ๋Œ€๊ทœ๋ชจ ํŠธ๋ž˜ํ”ฝ ํ™˜๊ฒฝ์—์„œ๋„ ์•ˆ์ •์ ์ด๊ณ  ํšจ์œจ์ ์ธ ์„œ๋น„์Šค๋ฅผ ์šด์˜ํ•  ์ˆ˜ ์žˆ๋Š” ์ฒด๊ณ„๋ฅผ ๊ตฌ์ถ•ํ•˜๋Š” ๊ฒƒ์„ ๋ชฉํ‘œ๋กœ ํ•˜๊ฒ ์Šต๋‹ˆ๋‹ค.


0๊ฐœ์˜ ๋Œ“๊ธ€