ํ๋ก์ ํธ ๋ชฉํ
๋ณธ ํ๋ก์ ํธ๋ ์ง๊ธ๊น์ง ํ์ตํ JDBC, MVC ํจํด, MySQL, Docker ๊ธฐ์ ์ ์ข ํฉ์ ์ผ๋ก ๋ณต์ตํ๊ณ , n8n ์๋ํ ๋๊ตฌ๋ฅผ ์๋กญ๊ฒ ๋์ ํ์ฌ ๊ฐ์์ ์นด๋ ๊ฒฐ์ ์๋ฆผ ์์คํ ์ ๊ตฌ์ถํ๋ ๊ฒ์ ๋ชฉ์ ์ผ๋ก ํ๋ค.
์ด๋ฅผ ํตํด ๋ฐฑ์๋ ๋ก์ง ๊ตฌํ๋ถํฐ ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๋, ์ปจํ ์ด๋ ํ๊ฒฝ์์์ ์ด์, ๊ทธ๋ฆฌ๊ณ DevOps ๊ด์ ์ ๋ก๊ทธ ๋ชจ๋ํฐ๋ง ๋ฐ ์๋ฆผ ์๋ํ๊น์ง์ ์ ๊ณผ์ ์ ๊ฒฝํํ๋ค.
| ์ฌ์ฌํ | ์ด๋ช ์ง | ์ด์ฑ์ |
| GitHub | GitHub | GitHub |
์นด๋ ๊ฒฐ์ ์ ๋ฐ์ํ๋ ๋ก๊ทธ ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๊ณ , ์ด๋ฅผ ๋ชจ๋ํฐ๋งํ์ฌ Slack์ผ๋ก ๊ฒฐ์ ๋ด์ญ(์ฑ๊ณต/์คํจ)์ ์ค์๊ฐ ๋ฐ์กํ๋ ์์คํ ์ด๋ค.
- ๐ฆ ๊ธ์ต IT ๋๋ฉ์ธ ์ค์ต: ์ฐ๋ฆฌ FISA(๊ธ์ต IT ์์นด๋ฐ๋ฏธ)์ ํน์ฑ์ ์ด๋ ค ๊ฐ์ด ๊ฒฐ์ ์์คํ ์ ๊ตฌํํ๋ค.
- ๐ ๋ฐฐ์ด ์ง์์ ํ์ฉ: Java๋ก ๋น์ฆ๋์ค ๋ก์ง์ ๊ตฌํํ๊ณ , JDBC๋ฅผ ํตํด MySQL๊ณผ ์ฐ๋ํ๋ค. ํนํ '๋ก๊ทธ(Log) ๊ธฐ๋ก'์ DB ๊ธฐ๋ฐ์ผ๋ก ๊ตฌํํ๋ค๋ ๊ฒ์ ์์๋ฅผ ๋๋ค.
- โพ๏ธ DevOps ๊ด์ ์ ์ฉ: ํด๋ผ์ฐ๋ ์์ง๋์ด๋ง ๊ณผ์ ์ ํน์ฑ์ ๋ง์ถฐ, ๋จ์ํ ๋ฐ์ดํฐ๋ฅผ ์๋ ๊ฒ์ ๋์ด ๋ก๊ทธ๋ฅผ ๋ถ์ํ๊ณ , ์ค๋ฅ๋ฅผ ํ์งํ์ฌ, ์๋ฆผ์ ๋ณด๋ด๋ ์๋ํ ํ์ดํ๋ผ์ธ์ ๊ตฌ์ถํ๋ค.
์ฌ์ฉ์๊ฐ ์ฝ๋๋ฅผ ๊ฑฐ์ ์์ฑํ์ง ์๊ณ ๋ ๋ค์ํ ์ฑ๊ณผ ์๋น์ค๋ฅผ ์ฐ๊ฒฐํด ์ ๋ฌด๋ฅผ ์๋ํํ ์ ์๋ ์ํฌํ๋ก์ฐ ์๋ํ ๋๊ตฌ์ด๋ค. ๋ ธ๋(Node) ๊ธฐ๋ฐ์ผ๋ก ๊ฐ ๊ธฐ๋ฅ์ ์ฐ๊ฒฐํ์ฌ ์์ ์์๋ฅผ ์ ์ํ ์ ์๋ค.
- DevOps & Backend ์๋ํ: ์๋ฒ ๋ชจ๋ํฐ๋ง ์๋ฆผ(CloudWatch, Grafana ์ฐ๋), ๊ฐ๋จํ ๋ฐฑ์๋ API ๊ตฌ์ฑ, GitHub ์ด์ ์ฐ๋ ๋ฑ ๊ฐ๋ฐ ์ด์ ํจ์จ์ ๋์ด๋ ๋ฐ ์ต์ ํ๋์ด ์๋ค.
- ๋ฌด๋ฃ ์๊ฐ ํธ์คํ : Docker ๋ฑ์ ํตํด ๋ด ์๋ฒ์ ์ง์ ์ฌ๋ ค ์ฌ์ฉํ ๊ฒฝ์ฐ ๋น์ฉ ์์ด ํ์ฉ ๊ฐ๋ฅํ๋ค.
- ๊ฐ๋ฐ์ ์นํ์ : ๋จ์ ์ฐ๊ฒฐ์ ๋์ด JavaScript๋ฅผ ํ์ฉํ ๋ณต์กํ ๋ฐ์ดํฐ ๊ฐ๊ณต์ด ๊ฐ๋ฅํ๋ค.
- MCP์์ ์ฐจ๋ณ์ : ์ต๊ทผ ํ๋์ธ MCP(Model-Context Protocol)๊ฐ AI์ ๋๊ตฌ์ ์ฐ๊ฒฐ ํ์ค์ด๋ผ๋ฉด, n8n์ ๊ตฌ์ฒด์ ์ธ ์์ ๋ก์ง(Workflow)์ ์คํํ๋ ๋ฐ ์ด์ ์ ๋ง์ถ๋ค.
๋ณธ ํ๋ก์ ํธ์ ๋ชจ๋ ์๋น์ค(MySQL, n8n)๋ Docker ์ปจํ ์ด๋ ํ๊ฒฝ ์์์ ๊ตฌ๋๋๋ค.
๊ฒฐ์ ์ ๋ณด๋ฅผ ์ ์ฅํ๊ธฐ ์ํ payment_logs ํ
์ด๋ธ ๊ตฌ์กฐ๋ ๋ค์๊ณผ ๊ฐ๋ค.
CREATE TABLE IF NOT EXISTS payment_logs (
log_id INT AUTO_INCREMENT PRIMARY KEY,
card_number VARCHAR(20), -- ์นด๋๋ฒํธ
amount DECIMAL(10, 0), -- ๊ฒฐ์ ๊ธ์ก
merchant_name VARCHAR(50), -- ๊ฐ๋งน์
status VARCHAR(10), -- SUCCESS, FAIL
message VARCHAR(100), -- ๋ก๊ทธ ๋ฉ์์ง (์๋ฌ ์์ธ ๋ฑ)
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP, -- ๊ฒฐ์ ์๊ฐ
is_alerted BOOLEAN DEFAULT FALSE -- Slack ์๋ฆผ ๋ฐ์ก ์ฌ๋ถ
);- ์คํ๋ฒ ์ค
- GS25
- ์ฟ ํก
- ๋ฐฐ๋ฌ์๋ฏผ์กฑ
- ์นด์นด์คํ์
- FISA ๊ตฌ๋ด์๋น
- ์์ก๋ถ์กฑ
- ํ๋์ด๊ณผ
- ๋๋๋ถ์ค์นด๋
- ํต์ ์๊ฐ์ด๊ณผ
- CVC์ค๋ฅ
| log_id | card_number | amount | merchant_name | status | message | created_at | is_alerted |
|---|---|---|---|---|---|---|---|
| 31 | 9448---1001 | 4000 | woori_atm | SUCCESS | ๊ฒฐ์ ์ ์ ์น์ธ | 2026-01-15 17:31:15 | FALSE |
| 32 | 4700---8910 | 12000 | cu | SUCCESS | ๊ฒฐ์ ์ ์ ์น์ธ | 2026-01-16 09:23:15 | FALSE |
| log_id | card_number | amount | merchant_name | status | message | created_at | is_alerted |
|---|---|---|---|---|---|---|---|
| 19 | 9448---1001 | 24000 | emart | FAIL | ์์ก๋ถ์กฑ | 2026-01-15 17:31:15 | TRUE |
| 20 | 4700---8910 | 3000 | cu | FAIL | ํ๋์ด๊ณผ | 2026-01-16 09:23:15 | TRUE |
| 21 | 9448---1001 | 13000 | woori_atm | FAIL | ๋๋๋ถ์ค์นด๋ | 2026-01-17 13:17:12 | TRUE |
| 33 | 9448---1001 | 500000 | netflix | FAIL | ํต์ ์๊ฐ์ด๊ณผ | 2026-01-17 13:17:12 | TRUE |
| 34 | 3200---1717 | 1700 | cu | FAIL | CVC์ค๋ฅ | 2026-01-17 13:17:12 | TRUE |
๐จ [๊ฒฐ์ ์ค๋ฅ ๋ฐ์]
์นด๋๋ฒํธ: 8384-****-****-6028
๊ธ์ก: 36000์
์ฌ์ : ๊ฒฐ์ ์น์ธ ๊ฑฐ์ ๋จ (ํต์ ์๊ฐ์ด๊ณผ)๊ฒฐ์ ์ฑ๊ณต
์นด๋๋ฒํธ: 7753-****-****-6977
๊ธ์ก: 85000์
๊ฒฐ์ ์๊ฐ: 2026-01-19 17:49:01- ubuntu server 24.04.3
# docker.io ์ค์น
sudo apt update
sudo apt install docker.io
# ๋ก๊ทธ์ธ(๋์ปค ํ๋ธ ๊ณ์ )
docker login
# docker-compose ์ค์น(์ฌ๋ฌ ์ปจํ
์ด๋๋ฅผ ํ ๋ฒ์ ๋ง๋ค์ด ์ฃผ๋ ๋๊ตฌ)
sudo apt install docker-composeversion: '3'
services:
# 1. MySQL ์ปจํ
์ด๋
mysql-db:
image: mysql:8.0
container_name: fisa-mysql
environment:
MYSQL_ROOT_PASSWORD: root # ๋ฃจํธ ๋น๋ฐ๋ฒํธ
MYSQL_DATABASE: fisa_db # ์์ํ ๋ ๋ง๋ค ๋ฐ์ดํฐ๋ฒ ์ด์ค ์ด๋ฆ
TZ: Asia/Seoul # ์๊ฐ๋๋ฅผ ํ๊ตญ์ผ๋ก ์ค์
ports:
- "3306:3306" # ์ธ๋ถ์ ์ํฌํธ:๋ด๋ถํฌํธ
volumes:
- ./mysql_data:/var/lib/mysql # DB ๋ฐ์ดํฐ ๋ณด๊ด ์ฅ์
command: --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci # ํ๊ธ ๊นจ์ง ๋ฐฉ์ง
# 2. n8n ์ปจํ
์ด๋
n8n:
image: n8nio/n8n
container_name: fisa-n8n
ports:
- "5678:5678"
environment:
- N8N_BASIC_AUTH_ACTIVE=true # ๋ก๊ทธ์ธ ๊ธฐ๋ฅ ์ผ๊ธฐ
- N8N_BASIC_AUTH_USER=admin # n8n ์์ด๋ - ์ค์ ์ฌ์ฉ๋์ง ์์
- N8N_BASIC_AUTH_PASSWORD=pass # n8n ๋น๋ฐ๋ฒํธ - ์ค์ ์ฌ์ฉ๋์ง ์์
- GENERIC_TIMEZONE=Asia/Seoul # ์๊ฐ๋ ์ค์
volumes:
- ./n8n_data:/home/node/.n8n # ์ํฌํ๋ก์ฐ ์ ์ฅ์์น
dns: # DNS ์ฃผ์ ์ง์
- 8.8.8.8 # ๊ตฌ๊ธ DNS 1 (Trouble Shooting 2 ์ฐธ๊ณ )
- 8.8.4.4 # ๊ตฌ๊ธ DNS 2
depends_on:
- mysql-db # DB๊ฐ ์ผ์ง ํ ์คํdocker-compose up -d
# ์ค์น ํ์ธ
# ์๋ ๋ช
๋ น์ด ์คํ์ fisa-mysql๊ณผ fisa-n8n ์ปจํ
์ด๋๊ฐ ํ์๋์ด์ผ ํจ
docker ps๐จ Trouble Shooting 1: ๊ถํ ๋ฌธ์
docker-compose up -d ํ n8n ์ปจํ ์ด๋๊ฐ ์คํ๋์ง ์๋๋ค๋ฉด, ๋ก์ปฌ์ ./n8n_data ๋๋ ํ ๋ฆฌ ๊ถํ ๋ฌธ์ ์ผ ๊ฐ๋ฅ์ฑ์ด ๋๋ค.
n8n์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ํฌํ๋ก์ฐ๋ฅผ ์ ์ฅํ๋ ./n8n_data ๋๋ ํ ๋ฆฌ๋ฅผ root ๊ถํ์ผ๋ก ๋ง๋ค๊ธฐ ๋๋ฌธ์ด๋ค.
์๋ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํด, ํด๋น ๋๋ ํ ๋ฆฌ์ ๋ชจ๋ ์ฌ์ฉ์ ๊ถํ์ ์ถ๊ฐํ์ฌ ๋ค์ ์๋ํ๋ค.
sudo chmod -R 777 ./n8n_data๐จ ์ฃผ์์ฌํญ
๊ฒ์คํธ IP๋ ๋ค๋ฅผ ์ ์๋ค. ๋ณธ์ธ ์์์ ip ์ฃผ์๋ฅผ ํํ ํ์ธํด์ผ ํ๋ค.
| ๊ท์น ๋ช | ํธ์คํธ IP | ํธ์คํธ ํฌํธ | ๊ฒ์คํธ IP(์์) | ๊ฒ์คํธ ํฌํธ | ๋น๊ณ |
|---|---|---|---|---|---|
| mysql docker | 127.0.0.1 | 3306 | 10.0.2.15 | 3306 | |
| n8n docker | 127.0.0.1 | 5678 | 10.0.2.15 | 5678 |
docker exec -it fisa-mysql mysql -u root -p๋ช ๋ น์ผ๋ก ๋์ปค ์ปจํ ์ด๋์ mysql ์๋ก ์ด๋- ์๋ ์ฟผ๋ฆฌ ๋ฌธ์ ์ ๋ ฅํ์ฌ ํ ์ด๋ธ ์์ฑ
USE fisa_db;
CREATE TABLE IF NOT EXISTS payment_logs (
log_id INT AUTO_INCREMENT PRIMARY KEY,
card_number VARCHAR(20),
amount DECIMAL(10, 0),
merchant_name VARCHAR(50),
status VARCHAR(10),
message VARCHAR(100),
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
is_alerted BOOLEAN DEFAULT FALSE
);docker-compose up -d- ๋ธ๋ผ์ฐ์ ์ localhost:5678 ์ ๋ ฅ
- ๋ฉ์ผ, ๋น๋ฐ๋ฒํธ, ์ด๋ฆ ์ ๋ ฅํ์ฌ ๊ฐ์
์ด๋ฉ์ผ ์ธ์ฆ ๊ณผ์ ์ ์์ผ๋, ์๋ฌด๋ ๊ฒ๋ ๋ง๋ค์ด๋ ๊ด์ฐฎ๋ค.
- ์ข์ธก + ๋ฒํผ ํด๋ฆญ
- Credential ํด๋ฆญ
- mysql ๊ฒ์ ํ continue
- ์๋์ ๊ฐ์ด ์
๋ ฅ ํ save
- Host:
mysql-db๐จ ์ฃผ์์ฌํญ
docker์์์ mysql์ ์ฌ์ฉํ ๊ฒฝ์ฐ ๋ฐ๋์ host ์ด๋ฆ์ container ์ด๋ฆ์ด ๋ค์ด๊ฐ์ผ ํ๋ค. - Database:
fisa_db - User:
root - Password:
root - Port:
3306
- Host:
- Slack API ์ฌ์ดํธ ์ ์ -> Create New App -> From scratch.
- App Name: (์๋ฌด๊ฑฐ๋), ์ํฌ์คํ์ด์ค ์ ํ -> Create.
- ์ผ์ชฝ ๋ฉ๋ด OAuth & Permissions ํด๋ฆญ.
- ์๋๋ก ์คํฌ๋กคํด์ Scopes -> Bot Token Scopes์
chat:write๊ถํ ์ถ๊ฐ. - ๋งจ ์๋ก ์ฌ๋ ค์ Install to Workspace ํด๋ฆญ -> ํ์ฉ.
- ์์ฑ๋ Bot User OAuth Token (
xoxb-๋ก ์์ํ๋ ๊ฐ)์ ๋ณต์ฌ - n8n์ผ๋ก ๋์์์ Credentials -> Slack API ์ถ๊ฐ ->
Access Token๋์ ๋ถ์ฌ๋ฃ๊ณ ์ ์ฅ.
๐จ Trouble Shooting 2: Slack Webhook ์ฌ์ฉ์ ์ฃผ์์ฌํญ
n8n์ slack ๋ ธ๋๋ฅผ ์ฐ๊ฒฐํ๋ ๊ฒ์ด ์๋, HTTP Request ๋ ธ๋์ Slack API์์ ๋ฐ์ Webhook url๋ก ์ฐ๊ฒฐํ์ฌ ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ๋ ์๋ค.
๋ค๋ง ์ด ๋ฐฉ๋ฒ์ ์ฌ์ฉํ ๋, n8n ๋์ปค ์ปจํ ์ด๋๊ฐ DNS ์๋ฒ๋ฅผ ์ฐพ์ง ๋ชปํ๋ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
docker-compose down๋ช ๋ น์ผ๋ก ์ปจํ ์ด๋๋ฅผ ๋ฉ์ถ ํ,docker-compose.yml์ DNS ์ฃผ์๋ฅผ ์ถ๊ฐํ๊ณ ๋ค์ ์๋์ํค๋ ํด๊ฒฐ๋์๋ค.
์ด์ ๋ฌ๋ฆฌ n8n์ Slack ๋ ธ๋๋ฅผ ์ฐ๊ฒฐํ๋ ๋ฐฉ์์์๋ ๋ณ๋์ ์๋ฒ ์ค๋ฅ๊ฐ ๋ฐ์ํ์ง ์์๋ค.
๋ฐ๋ผ์, ๋ณธ ํ๋ก์ ํธ์์๋ HTTP Request ๋ ธ๋์ Slack webhook URL์ ์ฐ๊ฒฐํ๋ ๊ฒ์ด ์๋๋ผ, Slack ๋ ธ๋๋ฅผ ์ฐ๊ฒฐํ๋ ๋ฐฉ์์ ์ฌ์ฉํ๋ค.
-
๊ฒฐ์ ์ค๋ฅ ๋ฐ์ก ๋ ธ๋
-
schedule trigger๋ ธ๋ ์ถ๊ฐ- trigger interval:
minutes(๋ถ ๋จ์๋ก ์์ ๋ฐ๋ณต) - seconds between triggers:
1(1๋ถ ๊ฐ๊ฒฉ์ผ๋ก ์ค์ )
- trigger interval:
-
mysql: execute a sql query๋ ธ๋ ์ถ๊ฐ-
Query ์นธ์ ์๋ sql์ ์ ๋ ฅ
-- ๊ฒฐ์ ์คํจ ๊ธฐ๋ก์ DB์์ ๊ฐ์ ธ์ด SELECT * FROM payment_logs WHERE (status = 'FAIL' OR status = 'ERROR') AND is_alerted = 0;
-
-
slack: send a message๋ ธ๋ ์ถ๊ฐ-
Credential: ๋ฐฉ๊ธ ๋ง๋ Slack bot ๊ณ์ ์ ํ
-
Resource:
Message, Operation:Send, Send message to :Channel -
Channel: ์๋ฆผ ๋ฐ์ ์ฑ๋ ์ ํ
-
Message Type:
Simple text message์ ํ ํ ์๋ ํ ์คํธ ์ ๋ ฅ๐จ [๊ฒฐ์ ์ค๋ฅ ๋ฐ์] ์นด๋๋ฒํธ: {{ $json.card_number }} ๊ธ์ก: {{ $json.amount }}์ ์ฌ์ : {{ $json.message }}
-
-
mysql: execute a sql query๋ ธ๋ ์ถ๊ฐ-
Query ์นธ์ ์๋ sql์ ์ ๋ ฅ
-- ์๋ฆผ ๋ณด๋ธ ๋ก๊ทธ ์ฒ๋ฆฌ์๋ฃ ํ์ UPDATE payment_logs SET is_alerted = 1 WHERE log_id = {{ $('Execute a SQL query').item.json.log_id }};
-
-
-
๊ฒฐ์ ์ฑ๊ณต ๋ฐ์ก ๋ ธ๋
-
schedule trigger๋ ธ๋ ์ถ๊ฐ- trigger interval:
minutes(๋ถ ๋จ์๋ก ์์ ๋ฐ๋ณต) - seconds between triggers:
10(10๋ถ ๊ฐ๊ฒฉ์ผ๋ก ์ค์ )
- trigger interval:
-
mysql: execute a sql query๋ ธ๋ ์ถ๊ฐ-
Query ์นธ์ ์๋ sql์ ์ ๋ ฅ
-- ๊ฒฐ์ ์ฑ๊ณต ๊ธฐ๋ก์ DB์์ ๊ฐ์ ธ์ด SELECT * FROM payment_logs WHERE (status = 'SUCCESS') AND is_alerted = 0;
-
-
slack: send a message๋ ธ๋ ์ถ๊ฐ-
Credential: ๋ฐฉ๊ธ ๋ง๋ Slack bot ๊ณ์ ์ ํ
-
Resource:
Message, Operation:Send, Send message to :Channel -
Channel: ์๋ฆผ ๋ฐ์ ์ฑ๋ ์ ํ
-
Message Type:
Simple text message์ ํ ํ ์๋ ํ ์คํธ ์ ๋ ฅ๊ฒฐ์ ์ฑ๊ณต ์นด๋๋ฒํธ: {{ $json.card_number }} ๊ธ์ก: {{ $json.amount }}์ ๊ฒฐ์ ์๊ฐ: {{ $json.created_at }}
-
-
mysql: execute a sql query๋ ธ๋ ์ถ๊ฐ-
Query ์นธ์ ์๋ sql์ ์ ๋ ฅ
-- ์๋ฆผ ๋ณด๋ธ ๋ก๊ทธ ์ฒ๋ฆฌ์๋ฃ ํ์ UPDATE payment_logs SET is_alerted = 1 WHERE log_id = {{ $('Execute a SQL query1').item.json.log_id }};
-
-
์ด ํ๋ก์ ํธ์ ์ ์ฉ๋ ๋๋ค ๋ก์ง์ ์ค์ ๊ฒฐ์ ํ๊ฒฝ์์ ์ผ์ด๋ ๋ฒํ ๋ค์ํ ์ํฉ์ ์๋ฎฌ๋ ์ด์ ํ๊ธฐ ์ํด ์ค๊ณ๋์๋ค.
๋จ์ํ ๋๋ฏธ ๋ฐ์ดํฐ ์ฝ์ ์ด ์๋, ์ค์ ์กด์ฌํ ๋ฒํ ๋ฐ์ดํฐ๋ค์ ๋ง๋ค์ด ๋น์ฆ๋์ค ๋ก์ง์ ๋ง๋๋ก ์์ฑํ ๋ค SQL๋ฌธ์ผ๋ก DB์ ์ฝ์ ํ๋ ๊ฒ์ด ๋ชฉ์ ์ด๋ค.
์๋๋ ์๋ฐ์ java.util.Random ํด๋์ค๋ฅผ ์ฌ์ฉํ์ฌ ๊ตฌํ๋ 4๊ฐ์ง ํต์ฌ ๋ก์ง์ด๋ค.
์ด ํ๋ก์ ํธ์์ ๊ฐ์ฅ ์ค์ํ ๋ถ๋ถ์ด๋ค. n8n์ด "์๋ฌ๋ฅผ ๊ฐ์งํ๊ณ ์ฌ๋์ ๋ณด๋ด๋์ง" ํ ์คํธํ๋ ค๋ฉด, ๊ฐ๋ ์๋ฌ๊ฐ ํฐ์ ธ์ผ ํ๋ค.
boolean isFail = random.nextInt(5) == 0;- ์๋ ์๋ฆฌ:
random.nextInt(5)๋0์ด์5๋ฏธ๋ง์ ์ ์๋ฅผ ๋ฐํํ๋ค. (์ฆ, 0, 1, 2, 3, 4 ์ค ํ๋)- 5๊ฐ์ ์ซ์ ์ค
0์ด ๋์ฌ ํ๋ฅ ์ ์ํ์ ์ผ๋ก 1/5, ์ฆ 20% ์ด๋ค.
- ์๋ฏธ:
- ๊ฒฐ์ 5๊ฑด ์ค ์ฝ 1๊ฑด ๊ผด๋ก ๊ณ ์์ ์ธ ๊ฒฐ์ ์คํจ(FAIL)๋ฅผ ๋ฐ์์ํจ๋ค.
- ๋๋จธ์ง 80%๋ ์ ์ ์น์ธ(SUCCESS)์ผ๋ก ์ฒ๋ฆฌ๋๋ค.
- ์ด ๋น์จ์ ์กฐ์ ํ๊ณ ์ถ๋ค๋ฉด
nextInt(10)์ผ๋ก ๋ฐ๊พธ๋ฉด 10% ํ๋ฅ ์ด ๋๊ณ ,nextInt(5) <= 1;๊ณผ ๊ฐ์ด ์กฐ๊ฑด๋ฌธ ๋ฒ์๋ฅผ ์กฐ์ ํ๋ฉด 40%๊ฐ ๋๋ค.
1์, 2์ ๊ฐ์ ๋นํ์ค์ ์ธ ๊ธ์ก์ด ์๋๋ผ, ์ค์ ์นด๋ ๊ฒฐ์ ์ฒ๋ผ ๋ณด์ด๋ ๊ธ์ก์ ๋ง๋ ๋ค.
int amount = (random.nextInt(100) + 1) * 1000;- ์๋ ์๋ฆฌ:
nextInt(100): 0๋ถํฐ 99๊น์ง์ ๋๋คํ ์ ์๊ฐ ๋์จ๋ค.+ 1: 0์์ ๋ฐฉ์งํ๊ณ ์ต์ 1๋จ์๋ฅผ ๋ง๋ ๋ค. (1~100)1000: ์ฒ ์ ๋จ์๋ก ๋ง๋ ๋ค.
- ๊ฒฐ๊ณผ: ์ต์ 1,000์์์ ์ต๋ 100,000์ ์ฌ์ด์ ๊ธ์ก์ด 1,000์ ๋จ์๋ก ์์ฑ๋๋ค. (์: 15,000์, 42,000์)
์ค์ ์กด์ฌํ๋ ์ํธ๋ช ๊ณผ ์กด์ฌํ ๋ฒํ ์ค๋ฅ๋ช ๋ค์ ๋ฏธ๋ฆฌ ๋ง๋ค์ด ๋ ๋ฐฐ์ด์์ ๋๋ค์ผ๋ก ๋ฝ๊ณ , ์กฐํฉํ์ฌ ๊ฒฐ์ ๊ธฐ๋ก์ ์์ฑํ๋ค.
String[] merchants = {"์คํ๋ฒ
์ค", "GS25", "์ฟ ํก", ...};
return merchants[random.nextInt(merchants.length)];- ์๋ ์๋ฆฌ:
- ๋ฐฐ์ด์ ๋ฐ์ดํฐ๊ฐ 6๊ฐ ์๋ค๋ฉด
length๋ 6์ด๋ค. nextInt(6)์ 0๋ถํฐ 5๊น์ง์ ๋๋คํ ์ ์๊ฐ ๋์จ๋ค.merchants[2]์ ๊ฐ์ ํํ๋ก, ๋๋คํ index์ ๊ฐ๊ฒ ์ด๋ฆ์ ๊บผ๋ธ๋ค.
- ๋ฐฐ์ด์ ๋ฐ์ดํฐ๊ฐ 6๊ฐ ์๋ค๋ฉด
- ๊ฒฐ๊ณผ: 6๊ฐ์ ๊ฐ๋งน์ ์ค ํ๋๊ฐ ๋๋ค์ผ๋ก ๋์จ๋ค.
- ์๋ฌ ์ฝ๋("์์ก๋ถ์กฑ", "ํ๋์ด๊ณผ" ๋ฑ) ๋ ๋๊ฐ์ ๋ฐฉ์์ผ๋ก ์ ํ๋๋ค.
๊ฐ์ธ์ ๋ณด ๋ณดํธ๋ฅผ ์ํด ๊ฐ์ด๋ฐ ์ซ์๋ฅผ ๊ฐ๋ฆฐ(Masking) ์นด๋ ๋ฒํธ๋ฅผ ์์ฑํ๋ค.
String.format("%04d-****-****-%04d",
random.nextInt(9000) + 1000, // ์ 4์๋ฆฌ
random.nextInt(9000) + 1000); // ๋ค 4์๋ฆฌ- ์๋ ์๋ฆฌ:
nextInt(9000): 0๋ถํฐ 8999๊น์ง์ ๋๋คํ ์ ์๊ฐ ๋์จ๋ค.+ 1000: ๊ทธ ๊ฒฐ๊ณผ์ ๋ํ๋ฉด 1000 ~ 9999 ๋ฒ์์ ์ ์๊ฐ ๋๋ค. (๋ฌด์กฐ๊ฑด 4์๋ฆฌ ์ซ์ ๋ณด์ฅ)
- ๊ฒฐ๊ณผ:
1234-****-****-5678ํํ์ ๋๋ค ์นด๋ ๋ฒํธ ๋ฌธ์์ด์ด ์์ฑ๋๋ค.
์ด Java ์ฝ๋๊ฐ ์คํ๋๋ ๋์ ๋ค์๊ณผ ๊ฐ์ ์ผ์ด ๋ฌด์์๋ก ๋ฐ๋ณต๋๋ค.
- 80% ํ๋ฅ ๋ก: "์คํ๋ฒ ์ค"์์ "15,000์" ๊ฒฐ์ ์ฑ๊ณต (๋ก๊ทธ: INFO) โ DB ์ ์ฅ โ n8n์์ ์ฑ๊ณต ํ์ดํ๋ผ์ธ ์๋ โ ์ฌ๋ ๋ฉ์์ง ๋ฐ์ก
- 20% ํ๋ฅ ๋ก: "์ฟ ํก"์์ "52,000์" ๊ฒฐ์ ์คํจ, ์ฌ์ ๋ "์์ก๋ถ์กฑ" (๋ก๊ทธ: WARN) โ DB ์ ์ฅ โ n8n์์ ์คํจ ํ์ดํ๋ผ์ธ ์๋ โ ๐จ ์ฌ๋ ๋ฉ์์ง ๋ฐ์ก
MVC(Model-View-Controller) ํจํด์ ์ ํ๋ฆฌ์ผ์ด์ ์ ๊ตฌ์ฑ ์์๋ฅผ ์ธ ๊ฐ์ง ์ญํ ๋ก ๊ตฌ๋ถํ์ฌ ๊ฐ๋ฐ ๋ฐ ์ ์ง๋ณด์์ ํจ์จ์ฑ์ ๋์ด๋ ์ํํธ์จ์ด ๋์์ธ ํจํด์ด๋ค.
- Model (๋ชจ๋ธ): ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ฐ์ดํฐ์ ๋น์ฆ๋์ค ๋ก์ง์ ๋ด๋นํ๋ค. ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ์ํ๋ฅผ ๊ด๋ฆฌํ๊ณ , ์์คํ ์ ํต์ฌ ๊ท์น์ ์ฒ๋ฆฌํ๋ ๋ถ๋ถ์ด๋ค.
- View (๋ทฐ): ์ฌ์ฉ์์๊ฒ ๋ณด์ฌ์ง๋ ํ๋ฉด(UI) ์ ๋ด๋นํ๋ค. ๋ชจ๋ธ์ด ์ฒ๋ฆฌํ ๋ฐ์ดํฐ๋ฅผ ์๊ฐ์ ์ผ๋ก ํํํ๊ฑฐ๋, ์ฌ์ฉ์์๊ฒ ์๋ฆผ์ ์ถ๋ ฅํ๋ ์ญํ ์ด๋ค.
- Controller (์ปจํธ๋กค๋ฌ): ์ฌ์ฉ์์ ์์ฒญ(Input) ์ ๋ฐ์ ๋ชจ๋ธ๊ณผ ๋ทฐ๋ฅผ ์ฐ๊ฒฐํ๋ ์งํ์ ์ญํ ์ ํ๋ค.
๋ฌด์์ ์ฒ๋ฆฌํ ์ง ๋ชจ๋ธ์๊ฒ ์ง์ํ๊ณ , ๊ทธ ๊ฒฐ๊ณผ๋ฅผ ์ด๋ค ๋ทฐ๋ก ๋ณด์ฌ์ค์ง ๊ฒฐ์ ํ๋ค.
- ๊ด์ฌ์ฌ์ ๋ถ๋ฆฌ (Separation of Concerns)
- ๋๋ค ๋ฐ์ดํฐ๋ฅผ ์์ฑํ๋ ๋ก์ง(Service), DB์ ์ ์ฅํ๋ ๋ก์ง(DAO), ๊ฒฐ๊ณผ๋ฅผ ์ถ๋ ฅํ๋ ๋ก์ง(View)์ด ์์ด์ง ์๋๋ก ๋ถ๋ฆฌํ์ฌ ์ฝ๋์ ๊ฐ๋ ์ฑ์ ๋์ธ๋ค.
- ์ ์ง๋ณด์ ๋ฐ ํ์ฅ์ฑ
- ์ถํ DB๊ฐ ๋ณ๊ฒฝ๋๋๋ผ๋ ๋น์ฆ๋์ค ๋ก์ง(Service)์ ์์ ํ ํ์ ์์ด DAO๋ง ์์ ํ๋ฉด ๋๋ค.
- ์ฝ์ ์ถ๋ ฅ ๋์ ์น ํ๋ฉด์ผ๋ก ํ์ฅํ๋๋ผ๋ View ๊ณ์ธต๋ง ๊ต์ฒดํ๋ฉด ๋๋ฏ๋ก ์ ์ฐํ ํ์ฅ์ด ๊ฐ๋ฅํ๋ค.
- ํ์ฌ ํ๋ก์ ํธ๋ view๋ฅผ ๋จ์ํ ์ฝ์ ์ถ๋ ฅ์ผ๋ก ๊ตฌํํ์๋ค.
- ํ์
ํจ์จ์ฑ
- ๋ฐ์ดํฐ ๊ตฌ์กฐ(DTO)์ ๋ก์ง(Service), ํ๋ฉด(View)์ด ๋ถ๋ฆฌ๋์ด ์์ด, ๊ฐ ์ปดํฌ๋ํธ๋ณ๋ก ๋ช ํํ ์ญํ ์ ์๊ฐ ๊ฐ๋ฅํ๋ค.
n8n_project
+---src
| +---controller
| | PaymentController.java
| |
| +---model
| | +---dao
| | | PaymentLogsDAO.java
| | |
| | +---domain
| | | PaymentLogsDTO.java
| | | RanDataDTO.java
| | |
| | \---service
| | PaymentService.java
| |
| +---util
| | DBUtil.java
| | RandomUtil.java
| |
| \---view
| PaymentFailView.java
| PaymentStartView.java
PaymentSuccessView.java
...
์์คํ
์ ํต์ฌ ์ฝ์ด๋ก์ ๋ฐ์ดํฐ์ ๋ก์ง์ ์ ๋ดํ๋ค. ๋ณธ ํ๋ก์ ํธ์์๋ model ํจํค์ง ํ์์ dao, domain, service๋ฅผ ๋์ด ๊ด๋ฆฌํ๋ค.
- domain (DTO)
PaymentLogsDTO.java: ๋ฐ์ดํฐ๋ฒ ์ด์ค์payment_logํ ์ด๋ธ๊ณผ 1:1๋ก ๋งคํ๋๋ ๋ฐ์ดํฐ ์ ์ก ๊ฐ์ฒด์ด๋ค.RanDataDTO.java: ๋๋ค ๋ฐ์ดํฐ ์ ์ฅ์ ์ํ ํ๋๋ฅผ ๋ด๋ ๊ฐ์ฒด์ด๋ค.
- dao (Data Access Object)
PaymentLogsDAO.java: ๋ฐ์ดํฐ๋ฒ ์ด์ค(MySQL)์ ์ง์ ์ฐ๊ฒฐํ์ฌINSERTSQL ์ฟผ๋ฆฌ๋ฅผ ์คํํ๋ ์ญํ ์ ์ํํ๋ค.
- service (Business Logic)
PaymentService.java:RandomUtil์ ์ด์ฉํด ๊ฒฐ์ ์ ๋ณด๋ฅผ ๋ฌด์์๋ก ์์ฑํ๊ณ , ์์ฑ๋ ๋ฐ์ดํฐ๋ฅผPaymentLogsDAO์๊ฒ ์ ๋ฌํ์ฌ ์ ์ฅ์ ์์ฒญํ๋ ํต์ฌ ๋น์ฆ๋์ค ๋ก์ง์ ์ํํ๋ค.
์ฌ์ฉ์(๊ฐ๋ฐ์/๊ด๋ฆฌ์)์๊ฒ ์์คํ ์ ๋์ ์ํ๋ฅผ ์๊ฐ์ ์ผ๋ก ์ ๋ฌํ๋ค.
PaymentStartView.java: ์ฑ์ ์์์ ์ผ๋ก, ์์์ ์๋ ค์ฃผ๋ ๋ฌธ์ฅ์ ์ถ๋ ฅํ๋ค.PaymentSuccessView.java: ๋ฐ์ดํฐ๊ฐ DB์ ์ ์์ ์ผ๋ก ์ ์ฅ๋์์ ๋, ์ฑ๊ณต ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋ค.PaymentFailView.java: DB ์ฐ๊ฒฐ ์คํจ๋ ์ ์ฅ ์ค๋ฅ ๋ฐ์ ์, ์๋ฌ ๋ฉ์์ง๋ฅผ ์ถ๋ ฅํ๋ค.
๋ชจ๋ธ๊ณผ ๋ทฐ๋ฅผ ์ฐ๊ฒฐํ๋ฉฐ, ํ๋ก์ ํธ์ ์ ์ฒด ํ๋ฆ์ ์ ์ดํ๋ค.
PaymentController.javaPaymentService๋ฅผ ํธ์ถํ์ฌ ๋น์ฆ๋์ค ๋ก์ง(๋ฐ์ดํฐ ์์ฑ ๋ฐ ์ ์ฅ)์ ์ํํ๋๋ก ์ง์ํ๋ค.- ์ํ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ
PaymentSuccessView๋๋PaymentFailView๋ฅผ ์ ํํ์ฌ ๊ฒฐ๊ณผ๋ฅผ ํ๋ฉด์ ๋์ด๋ค.
ํ๋ก์ ํธ ์ ๋ฐ์์ ๊ณตํต์ ์ผ๋ก ์ฌ์ฉ๋๋ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ค.
DBUtil.java: JDBC ๋๋ผ์ด๋ฒ ๋ก๋ฉ ๋ฐ Connection ๊ฐ์ฒด ์์ฑ/๋ฐํ ๋ฑ ์ค๋ณต๋๋ DB ์ฐ๊ฒฐ ์ฝ๋๋ฅผ ๋ด๊ณ ์๋ค.RandomUtil.java: ์์ ์ด๋ฆ, ๊ฒฐ์ ์ํ, ์นด๋ ๋ฒํธ ๋ฑ์ ๋ฌด์์๋ก ์ถ์ถํ๋ ๋ก์ง์ ์ ๊ณตํ์ฌService์ ์ฝ๋๋ฅผ ๊ฐ๊ฒฐํ๊ฒ ๋ง๋ ๋ค.
Java ํ๋ก๊ทธ๋จ์ด ์คํ๋๋ ๋์ ์ ์์ ์ผ๋ก MySQL์ ์นด๋ ๊ฒฐ์ ๊ธฐ๋ก์ด ์ ์ฅ๋๊ณ , n8n ์ํฌํ๋ก์ฐ๊ฐ ์ ์์ ์ผ๋ก ์๋๋๋ค.
๋ํ ์ฃผ๊ธฐ์ ์ผ๋ก Slack ์ฑ๋์ ๊ฒฐ์ ๊ธฐ๋ก์ด ๋์ฐฉํ๊ณ , ๊ฒฐ์ ์ค๋ฅ ๋ฐ์์ ์ฆ์ ๋ฉ์์ง๊ฐ ๋์ฐฉํ๋ ๋ชจ์ต์ด๋ค.

