โป ๋ณธ ํ๋ก์ ํธ๋ ๊ธฐ์กด JSP ๋ทฐ ๋ฐฉ์์์ ๋ฒ์ด๋ BackEnd์ FrontEnd๋ฅผ ๋ถ๋ฆฌํ๊ธฐ ์ํ ์์ ํ์ผ๋ก ์ฐธ๊ณ ๋ง ํ์๊ธธ ๋ฐ๋๋๋ค.
๋จ์ ํํ์ด์ง ๊ธฐ๋ฅ ๊ตฌํ ์ ํ์์ ์ธ ๋ถ๋ถ๋ง ์ฌ์ฉ ๊ฐ๋ฅํ๋๋ก ๊ฒฝ๋ํ ๋ ์คํํ๊ฒฝ ์ ๊ณต
๋ฉ์ธ ํ์ด์ง, ์ฌ์ฉ์ ๊ด๋ฆฌ, ๊ณต์ง์ฌํญ ๊ด๋ฆฌ, ๊ฒ์ํ ๊ด๋ฆฌ, ์๋ด ๊ด๋ฆฌ ๊ธฐ๋ฅ์ ์ ๊ณต
- ์ต์ด ๊ด๋ฆฌ์ ๊ณ์ ์ค์ ์ [ ๋ก๊ทธ์ธ๊ณ์ : admin , ๋ก๊ทธ์ธ์ํธ : Admin@1234 ] ๋ก ์ค์ ๋์ด ์๋ค.
- ๋ฉ์ธ ํ๋ฉด ์ฐ์ธก ์๋จ์ ํ์๊ฐ์ ๋ฒํผ์ ํตํด ์ฌ์ฉ์ ๊ณ์ ์ ์์ฑ ๊ฐ๋ฅํ๋ค.
- ๊ธฐ๋ณธ ๊ธฐ๋ฅ์ด๋ ์์ ๋ฉ๋ด๋ฅผ ์ค๋ฌด์ ์ผ๋ก ์ถ๊ฐ ์ปค์คํฐ๋ง์ด์งํ์ฌ ํ์ฉํ ์ ์๋ค.
- ์ต์ด ๊ด๋ฆฌ์ ๊ณ์ ์ ํตํ ๋ก๊ทธ์ธ์ด ๊ฐ๋ฅํ๋ค.
- ํ์๊ฐ์ ๋ฒํผ์ ํตํด ์์ฑํ ์ฌ์ฉ์ ๊ณ์ ์ ํตํ ๋ก๊ทธ์ธ์ด ๊ฐ๋ฅํ๋ค. (์ฌ์ฉ์ ๊ณ์ ์ ์ผ๋ถ ๋ฉ๋ด ์ ๊ทผ์ด ์ ํ๋๋ค)
- ๋ก๊ทธ์ธ ์ฐฝ ํ๋จ์ ์์ ๋ก๊ทธ์ธ ๋ฒํผ์ผ๋ก ๋ค์ด๋ฒ ๋ฐ ์นด์นด์ค ๊ณ์ ์ผ๋ก ๋ก๊ทธ์ธ์ด ๊ฐ๋ฅํ๋ค. ์ด ๊ฒฝ์ฐ์ ๊ถํ์ ์ฌ์ฉ์ ๊ณ์ ๊ณผ ๋์ผํ๋ค.
- ์์
๋ก๊ทธ์ธ ๊ธฐ๋ฅ์ ์ฌ์ฉ์ ์ํด์๋ ์ฌ์ ์ ๋ค์ด๋ฒ ๊ฐ๋ฐ์ ์ผํฐ ๋ฐ ์นด์นด์ค ๊ฐ๋ฐ์ ์ผํฐ ์์ Client IDยทClient Secret ๋ฐ๊ธ ๋ฐ Callback URL ๋ฑ๋ก์ ์งํํ ๋ค, ๋ฐ์ ๊ฐ์ ์๋์ ๊ฐ์ด ์
๋ ฅํ๋ค.
- ๋ค์ด๋ฒ โ ๋ฐฑ์๋
application.properties์Sns.naver.clientId,Sns.naver.clientSecret,Sns.naver.callbackUrl/ ํ๋ก ํธ์๋.env.development์VITE_APP_NAVER_CLIENTID,VITE_APP_NAVER_CALLBACKURL - ์นด์นด์ค โ ๋ฐฑ์๋
application.properties์Sns.kakao.clientId,Sns.kakao.callbackUrl/ ํ๋ก ํธ์๋.env.development์VITE_APP_KAKAO_CLIENTID,VITE_APP_KAKAO_CALLBACKURL - โป Client Secret ์ ๋ฐฑ์๋์๋ง ๋ฑ๋ก (ํ๋ก ํธ์๋ ๋ฒ๋ค์ ํฌํจ๋๋ฉด ์ ๋จ). ์นด์นด์ค๋ ๋ณธ ํ๋ก์ ํธ์์ Secret ๋ฏธ์ฌ์ฉ.
- โป OAuth CSRF ๋ฐฉ์ด์ฉ
state๊ฐ์ ๋ก๊ทธ์ธ ์๋ ์ ์๋์ผ๋ก ๋ฌด์์ ์์ฑ๋๋ฏ๋ก ๋ณ๋ ํ๊ฒฝ๋ณ์ ์ค์ ๋ถํ์.
- ๋ค์ด๋ฒ โ ๋ฐฑ์๋
- ํ๋ก ํธ์๋ ํ๊ฒฝ ์ค์ ํ์ผ์ ๊ฐ๋ฐ ํ๊ฒฝ์
.env.development, ๋น๋ ์ฐ์ถ๋ฌผ ํ๊ฒฝ์.env.production์ ์ฌ์ฉํ๋ค. ๋ฐฑ์๋ ํ๊ฒฝ ์ค์ ํ์ผ์ ์ฌํ ํํ์ด์ง Backend ์application.properties๋ฅผ ์ฐธ๊ณ ํ๋ค. - ๋ค์ด๋ฒ ์์ ๋ก๊ทธ์ธ์ ๋ํ ์์ธ ์ฌํญ์ ๋ค์ด๋ฒ ๋ก๊ทธ์ธ API ๋ฌธ์ ๋ฅผ ์ฐธ์กฐ ๊ฐ๋ฅํ๋ค.
- ์นด์นด์ค ์์ ๋ก๊ทธ์ธ์ ๋ํ ์์ธ ์ฌํญ์ ์นด์นด์ค ๋ก๊ทธ์ธ API ๋ฌธ์ ๋ฅผ ์ฐธ์กฐ ๊ฐ๋ฅํ๋ค.
- ํด๋น ํ๋ฉด ๋ฐ ์ธ๋ถ ๋ฉ๋ด ํ๋ฉด์ ๊ตฌ์ฑ์ ์ํ ์ํํ์ด์ง๊ฐ ์ ๊ณต๋๋ฉฐ ๊ธฐ๋ฅ์ ๊ตฌํ๋์ง ์์ ์ํ์ ๋๋ค.
- ์ธ๋ถ๋ฉ๋ด : ์ฌ์ดํธ์๊ฐ, ์ฐํ, ์กฐ์ง์๊ฐ, ์ฐพ์์ค์๋ ๊ธธ
- ๊ธฐ๋ฅ์ค๋ช : ์์ ๋ฉ๋ด์ ํด๋นํ๋ ํญ๋ชฉ์ผ๋ก ์ํ ํ์ด์ง ํํ๋ก ์กด์ฌํ๋ค.
- ํ์ฉ๋ฐฉ๋ฒ : ๊ฐ ์ํ ํ์ด์ง์ ๋ํ ์ฝํ ์ธ ๋ฅผ ์๋ก ๊ตฌ์ฑํ์ฌ ํ์ฉํ ์ ์๋ค.
- ํด๋น ํ๋ฉด ๋ฐ ์ธ๋ถ ๋ฉ๋ด ํ๋ฉด์ ๊ตฌ์ฑ์ ์ํ ์ํํ์ด์ง๊ฐ ์ ๊ณต๋๋ฉฐ ๊ธฐ๋ฅ์ ๊ตฌํ๋์ง ์์ ์ํ์ ๋๋ค.
- ์ธ๋ถ๋ฉ๋ด : ์ฃผ์์ฌ์ ์๊ฐ, ๋ํ์๋น์ค ์๊ฐ
- ๊ธฐ๋ฅ์ค๋ช : ์์ ๋ฉ๋ด์ ํด๋นํ๋ ํญ๋ชฉ์ผ๋ก ์ํ ํ์ด์ง ํํ๋ก ์กด์ฌํ๋ค.
- ํ์ฉ๋ฐฉ๋ฒ : ๊ฐ ์ํ ํ์ด์ง์ ๋ํ ์ฝํ ์ธ ๋ฅผ ์๋ก ๊ตฌ์ฑํ์ฌ ํ์ฉํ ์ ์๋ค.
- ํด๋น ํ๋ฉด ๋ฐ ์ธ๋ถ ๋ฉ๋ด ํ๋ฉด์ ๊ตฌ์ฑ์ ์ํ ์ํํ์ด์ง๊ฐ ์ ๊ณต๋๋ฉฐ ๊ธฐ๋ฅ์ ๊ตฌํ๋์ง ์์ ์ํ์ ๋๋ค.
- ์ธ๋ถ๋ฉ๋ด : ์๋ฃ์ค, ๋ฌป๊ณ ๋ตํ๊ธฐ, ์๋น์ค์ ์ฒญ
- ๊ธฐ๋ฅ์ค๋ช : ์์ ๋ฉ๋ด์ ํด๋นํ๋ ํญ๋ชฉ์ผ๋ก ์ํ ํ์ด์ง ํํ๋ก ์กด์ฌํ๋ค.
- ํ์ฉ๋ฐฉ๋ฒ : ๊ฐ ์ํ ํ์ด์ง์ ๊ธฐ๋ฅ์ ์ถ๊ฐ ๊ฐ๋ฐ ํ ๊ตฌ์ฑํ์ฌ ํ์ฉํ ์ ์๋ค.
- ์ธ๋ถ๋ฉ๋ด : ์ค๋์ํ์ฌ, ๊ธ์ฃผ์ํ์ฌ, ๊ณต์ง์ฌํญ, ์ฌ์ดํธ๊ฐค๋ฌ๋ฆฌ
- ๊ธฐ๋ฅ์ค๋ช : ๊ณตํต์ปดํฌ๋ํธ ์ผ์ ๊ด๋ฆฌ(๋ถ์์ผ์ )์ ๊ฒ์ํ ๊ธฐ๋ฅ์ ์ปค์คํฐ๋ง์ด์งํ์ฌ ์ฌ์ฉํ๋ค.
- ํ์ฉ๋ฐฉ๋ฒ : ๊ด๋ฆฌ์๊ฐ ๋ฑ๋กํ ์ผ์ ์ ๋ณด๋ฅผ ์กฐํํ๊ฑฐ๋ ๊ฒ์๋ฌผ์ ์กฐํํ ์ ์๋ค.
- ์ธ๋ถ๋ฉ๋ด : ์ผ์ ๊ด๋ฆฌ, ๊ฒ์ํ์์ฑ๊ด๋ฆฌ, ๊ฒ์ํ์ฌ์ฉ๊ด๋ฆฌ, ๊ณต์ง์ฌํญ ๊ด๋ฆฌ, ์ฌ์ดํธ ๊ฐค๋ฌ๋ฆฌ๊ด๋ฆฌ, ์ฌ์ดํธ๊ด๋ฆฌ์ ์ํธ๋ณ๊ฒฝ, ํ์๊ด๋ฆฌ
- ๊ธฐ๋ฅ์ค๋ช : ๊ณตํต์ปดํฌ๋ํธ ์ผ์ ๊ด๋ฆฌ(๋ถ์์ผ์ )๊ณผ ๊ฒ์ํ ๊ธฐ๋ฅ์ ์ปค์คํฐ๋ง์ด์งํ์ฌ ์ฌ์ฉํ๋ค.
- ํ์ฉ๋ฐฉ๋ฒ : ๊ด๋ฆฌ์๋ก ๋ก๊ทธ์ธ ํ ํ ์ผ์ ์ ๋ณด๋ฅผ ๋ฑ๋กํ๊ฑฐ๋ ๊ฒ์๋ฌผ์ ๋ฑ๋กํ ์ ์๋ค. (๊ฒ์ํ ์ค์ ๊ฐ๋ฅ)
ํ๋ก์ ํธ ๊ตฌ๋์ ํ์ํ ํ๊ฒฝ ํ๋ก๊ทธ๋จ ์ ๋ณด๋ ๋ค์๊ณผ ๊ฐ๋ค.
| ํ๋ก๊ทธ๋จ ๋ช | ๋ฒ์ ๋ช |
|---|---|
| Node.js | v20.19 ~ v22 LTS |
| NPM | v10 ์ด์ |
์ฌํ ํํ์ด์ง Backend ์์ค๋ฅผ ๋ฐ์ ๊ตฌ๋ํ๋ค.
์๋ 1 ~ 3์ ๊ณผ์ ์ ๋ฐ๋ผ์ ์งํํ๋ค.
Git์์ ๋ณต์ ํ์ฌ ์ค์น ์ 1-1. ์ ์ฐธ๊ณ ํ๋ค.
Git์์ clone ํ๋ค.
# ํ๋ก์ ํธ ์ ์ฅ์๋ฅผ ๋ก์ปฌ๋ก ๋ณต์
git clone https://github.com/[๊ณ์ ๋ช
]/egovframe-template-simple-react.git
# ๋ณต์ ๋ ํ๋ก์ ํธ ๋๋ ํ ๋ฆฌ๋ก ์ด๋
cd egovframe-template-simple-react
# node modules๋ฅผ ์ค์นํด ์ค๋ค.
npm installํ๋ก ํธ์๋๋ ๋ฐฑ์๋๋ฅผ ๋์ผ ์ถ์ฒ ์๋๊ฒฝ๋ก /api ๋ก ํธ์ถํ๋ค (src/config.js์ SERVER_URL, ๊ธฐ๋ณธ๊ฐ /api).
๊ฐ๋ฐ ์๋ฒ(npm run dev)๋ vite.config.js ์ ํ๋ก์ ์ค์ ์ด /api/* ์์ฒญ์ ๋ฐฑ์๋๋ก ์ ๋ฌํ๋ฏ๋ก,
๋ฐฑ์๋๊ฐ http://localhost:8080 ์์ ๊ตฌ๋ ์ค์ด๋ฉด ์ถ๊ฐ ์ค์ ์์ด ๋ฐ๋ก ๋์ํ๋ค.
๋ฐฑ์๋๊ฐ ๋ค๋ฅธ ํธ์คํธ/ํฌํธ์์ ๋ฐ ๋๋ง ์๋ ๋ณ์๋ฅผ ์ฌ์ฉํ๋ค.
# ๊ฐ๋ฐ ์๋ฒ์ ํ๋ก์ ๋์ ๋ณ๊ฒฝ (.env.development ๋๋ ์
ธ ํ๊ฒฝ๋ณ์)
VITE_APP_API_PROXY_TARGET=http://192.168.0.10:8080
# ๋น๋ ์ฐ์ถ๋ฌผ์ด ๋ฐฑ์๋๋ฅผ ์ ๋ URL๋ก ์ง์ ํธ์ถํด์ผ ํ ๋ (๋น๋ ์์ )
VITE_APP_API_BASE_URL=http://api.example.com์ปจํ ์ด๋/Kubernetes ๋ฐฐํฌ์์๋ nginx ๊ฐ
/api๋ฅผ ๋ฆฌ๋ฒ์ค ํ๋ก์ํ๋ฏ๋ก ์ ๋ณ์ ์์ด ๊ธฐ๋ณธ๊ฐ(/api)์ ๊ทธ๋๋ก ์ฐ๊ณ , ๋ฐฑ์๋ ์ฃผ์๋ ๋ฐํ์์BACKEND_URL๋ก ์ฃผ์ ํ๋ค. ์์ธํ ๋ด์ฉ์ ์๋ "์ปจํ ์ด๋ ๋ฐฐํฌ" ์ ์ ์ฐธ๊ณ ํ๋ค.
# ํ
์คํธ์ฉ ๋ฆฌ์กํธ ์๋ฒ๋ฅผ ์คํํ ๋ ์๋ ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ๋ค.
npm run dev# ๋น๋ํ ๋์๋ ์๋ ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ๋ค.
npm run build# ๋ก์ปฌ์์ ๋ฏธ๋ฆฌ๋ณด๊ธฐํ ๋๋ ์๋ ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ๋ค.
npm run preview# ํ
์คํธ ๋์์ vite.config.js ์ test.include ํจํด์ ๋ฐ๋ผ ์์ง๋๋ค.
# watch ๋ชจ๋๋ก ํ
์คํธ๋ฅผ ์คํํ ๊ฒฝ์ฐ์๋ ์๋ ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ๋ค.
npm run test
# ์ผํ์ฑ ํ
์คํธ๋ฅผ ์คํํ ๊ฒฝ์ฐ์๋ ์๋ ๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํ๋ค.
npm run test:run์ ์ ๋ฒ๋ค์ nginx ๋ก ํธ์คํ
ํ๋ ์ปจํ
์ด๋ ์ด๋ฏธ์ง์ Kubernetes ๋งค๋ํ์คํธ๋ฅผ ์ ๊ณตํ๋ค.
nginx ๊ฐ /api/* ์์ฒญ์ ๋ฐฑ์๋๋ก ๋ฆฌ๋ฒ์ค ํ๋ก์ํ๋ฏ๋ก, ํ๋ก ํธ์๋๋ ๋ฐฑ์๋ ์ฃผ์๋ฅผ ๋น๋ ์์ ์ ๊ณ ์ ํ์ง ์๊ณ
๋์ผ ์ถ์ฒ ์๋๊ฒฝ๋ก(/api) ๋ก ํธ์ถํ๋ค. ๋ฐฑ์๋ ์ฃผ์๋ ๋ฐํ์์ BACKEND_URL ํ๊ฒฝ๋ณ์๋ก ์ฃผ์
ํ๋ค.
API base ๋
src/config.js์SERVER_URL๋ก ๊ฒฐ์ ๋๋ฉฐ ๊ธฐ๋ณธ๊ฐ์/api์ด๋ค. ๋ณ๋ ํธ์คํธ๋ก ์ง์ ํธ์ถํด์ผ ํ๋ ๊ฒฝ์ฐ์๋ง ๋น๋ ์VITE_APP_API_BASE_URL๋ก ์ ๋ URL ์ ์ง์ ํ๋ค.
Dockerfile ์ heredoc(COPY <<'NGINX' ...) ๊ตฌ๋ฌธ์ ์ฌ์ฉํ๋ฏ๋ก BuildKit ์ด ํ์ํ๋ค.
Docker 20.10+ ์์๋ ๊ธฐ๋ณธ ํ์ฑํ๋์ด ์์ผ๋ฉฐ, ๋นํ์ฑ ํ๊ฒฝ์ด๋ผ๋ฉด DOCKER_BUILDKIT=1 ์ ์ง์ ํ๋ค.
DOCKER_BUILDKIT=1 docker build -t egovframe-template-simple-react:5.0.0 .# ์ธ๋ถ ๋ฐฑ์๋ ์ฃผ์๋ฅผ BACKEND_URL ๋ก ์ง์ (๊ธฐ๋ณธ๊ฐ: http://egov-simple-backend:8080)
BACKEND_URL=http://host.docker.internal:8080 docker compose up -d web
# http://localhost:3000/ ์ ์ (WEB_PORT ๋ก ๋ณ๊ฒฝ ๊ฐ๋ฅ)๋ฐฑ์๋ ์ด๋ฏธ์ง์ DB ์ด๊ธฐํ ์คํฌ๋ฆฝํธ๊ฐ ํ์ํ๋ค. ๋ค์์ ์ ์ ๋ก ํ๋ค.
- ์ฌํ ํํ์ด์ง Backend ๋ฅผ ๋ณธ ์ ์ฅ์์ ๊ฐ์ ์์ ๋๋ ํ ๋ฆฌ์ clone (DB ์ด๊ธฐํ SQL ๋ง์ดํธ ๊ฒฝ๋ก ๊ธฐ์ค, ๋ค๋ฅด๋ฉด
BACKEND_DB_DIR๋ก ์กฐ์ ) - ๋ฐฑ์๋ ์ด๋ฏธ์ง ์ฌ์ ๋น๋:
egovframe-template-simple-backend:5.0.0
# ๋ฐฑ์๋ ์ด๋ฏธ์ง ๋น๋ (๋ฐฑ์๋ ์ ์ฅ์์์)
git clone https://github.com/eGovFramework/egovframe-template-simple-backend.git
(cd egovframe-template-simple-backend && DOCKER_BUILDKIT=1 docker build -t egovframe-template-simple-backend:5.0.0 .)
# DB ๋น๋ฐ๋ฒํธ ๋ฑ .env ์์ฑ (ํ๋ก ํธ์๋ ์ ์ฅ์ ๋ฃจํธ)
cat > .env <<'ENV'
MYSQL_ROOT_PASSWORD=changeit_root
MYSQL_PASSWORD=changeit_app
ENV
# ํ๋ก ํธ(nginx) + ๋ฐฑ์๋ + MySQL ๋์ ๊ธฐ๋
docker compose --profile fullstack up -d
# http://localhost:3000/ ์ ์ ํ admin / Admin@1234 ๋ก ๋ก๊ทธ์ธ์ด ๊ตฌ์ฑ์์ ๋ธ๋ผ์ฐ์ โ nginx(/api/*) โ ๋ฐฑ์๋(:8080) โ MySQL ๊ฒฝ๋ก๋ก ๋ก๊ทธ์ธ๊ณผ ๊ฒ์ํ CRUD ๊ฐ ํ ๋ฒ์ ๋์ํ๋ค.
kubectl apply -f k8s/
# Service ๋ ClusterIP ์ด๋ฏ๋ก port-forward ๋ก ์ ์ํ๋ค.
kubectl port-forward svc/egov-simple-react 3000:8080
# http://localhost:3000/ ์ ์๋ฐฑ์๋ ์ฐ๋: k8s/deployment.yaml ์ BACKEND_URL ํ๊ฒฝ๋ณ์๊ฐ ๋ฐฑ์๋ ํ
ํ๋ฆฟ
(egovframe-template-simple-backend)์ k8s Service ์ด๋ฆ๊ณผ ์ผ์นํ๋๋ก http://egovframe-template-simple-backend:8080
์ผ๋ก ๊ธฐ๋ณธ ์ค์ ๋์ด ์๋ค. ๋ฐฑ์๋๋ฅผ ํจ๊ป ๋ฐฐํฌํ๋ฉด ๋ ๋งค๋ํ์คํธ์ ๊ธฐ๋ณธ๊ฐ๋ง์ผ๋ก ์ฐ๋๋๋ค. ๋ค๋ฅธ ๋ค์์คํ์ด์ค์
๋ฐฐํฌํ๋ ๊ฒฝ์ฐ FQDN ์ผ๋ก ์กฐ์ ํ๋ค(์: http://egovframe-template-simple-backend.<namespace>.svc.cluster.local:8080).
nginx ์ปจํ
์ด๋๋ readOnlyRootFilesystem ์ด๋ฏ๋ก ๊ธฐ๋ ์ BACKEND_URL ์นํ ๊ฒฐ๊ณผ๋ฅผ
emptyDir(/etc/nginx/conf.d)์ ๊ธฐ๋กํ๋ค.
๋ณด๋ค ์์ธํ ์ค๋ช ์ ์๋์ ๋ฌธ์๋ฅผ ํ์ธํ๋ค.







