Spring-Batch-Bulk-Mailing-Service๋ Spring Batch ํ๋ ์์ํฌ๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ง๋ ๋๋ ๋ฉ์ผ ์ ์ก ์์คํ ์ ๋๋ค.
๋ง์ ์ฌ์ฉ์์๊ฒ ์ผ๊ด์ ์ผ๋ก ๋์ผํ ๋ด์ฉ์ ๋ฉ์ผ์ ๋ณด๋ผ ๋ ์ฌ์ฉํ๋ฉด ๋ฉ๋๋ค. ์ฒญํฌ ๋จ์๋ก ์ฌ์ฉ์์๊ฒ ๋ฉ์ผ์ ๋ณด๋ด๋ฉฐ ์์์น ๋ชปํ ์์ธ๊ฐ ๋ฐ์ํด๋ Retry ๊ธฐ๋ฅ์ ํตํด ๋ฉ์ผ ์ ์ก์ ์ฌ์๋ ํฉ๋๋ค. ๊ฐ๋จํ MD ํฌ๋งทํ ์ ์ง์ํ๊ณ ์์ ๋ํ ๊น๋ํ๊ณ ์ ๋๋์ด ์์ต๋๋ค.
Spring-Batch๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค์ ๋ก๊ทธ๋ฅผ ๋จ๊ธธ ์ ์๋ ํ ์ด๋ธ์ด ์์ผ๋ฉด ์คํ์ด ๋์ง ์๊ธฐ ๋๋ฌธ์ ํ์์ ์ผ๋ก ๋ฐ์ดํฐ๋ฒ ์ด์ค๋ฅผ ์ค์น ํด์ผ ํฉ๋๋ค.
version: "3"
services:
db:
image: mysql:8.0
ports:
- "3306:3306"
environment:
MYSQL_ROOT_PASSWORD: 1234 # ์ฌ์ฉ์์ ๋ง๊ฒ MYSQL ROOT ๋น๋ฐ๋ฒํธ ์์
MYSQL_DATABASE: springBatch # ์ฌ์ฉ์์ ๋ง๊ฒ ์ด๋ฆ ์์
MYSQL_USER: user # ์ฌ์ฉ์์ ๋ง๊ฒ ์ด๋ฆ ๋ณ๊ฒฝ
MYSQL_PASSWORD: 1234 # ์ฌ์ฉ์์ ๋ง๊ฒ ๋น๋ฐ๋ฒํธ ๋ณ๊ฒฝ
TZ: Asia/Seouldocker-compose.yml ํ์ผ ์์ฑ ํ docker-compose up ๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํด ๋์ปค ์คํ
https://github.com/BDD-CLUB/BDD-Bulk-Mailing-Service.git
๋ฐ์ดํฐ๋ฒ ์ด์ค ์ฐ๊ฒฐ๊ณผ ์ด๋ฉ์ผ ์ฐ๋, API ์ฌ์ฉ์ ์ํ ํจ์ค์๋๋ฅผ ์ค์ ํ๊ธฐ ์ํด์ yml ํ์ผ์ ์ถ๊ฐํฉ๋๋ค.
ํ์ผ ๊ฒฝ๋ก๋ spring-batch > src > main > resources > application.yml ์ ๋๋ค.
spring:
mail:
host: smtp.gmail.com
port: 587
username: # fix
password: # fix
properties:
mail.smtp.starttls.enable: true
mail.smtp.auth: true
protocol: smtp
datasource:
url: jdbc:mysql://localhost:3306/{}?serverTimezone=Asia/Seoul&characterEncoding=UTF-8 # fix {}๋ ์
๋ ฅํ๊ณ ์ ๊ฑฐํ ๊ฒ.
username: user # fix
password: 1234 # fix
driver-class-name: com.mysql.cj.jdbc.Driver
batch:
jdbc:
initialize-schema: always
job:
enabled: false
name: ${job.name:NONE}
jpa:
hibernate:
ddl-auto: update
database-platform: org.hibernate.dialect.MySQL8Dialect
defer-datasource-initialization: true
properties:
hibernate:
format_sql: true
show-sql: true
์์ ์ค๋ช ๋๋ก DB์ ๋ก๊ทธ๋ฅผ ๋จ๊ธฐ๊ธฐ ์ํ ํ ์ด๋ธ์ด ์์ด์ผ ํ๋ฉฐ, ์์ฑํ๊ธฐ ์ํ SQL ๋ฌธ์ ์๋์ ๊ฐ์ต๋๋ค.
SELECT * FROM springBatch.BATCH_JOB_EXECUTION;CREATE TABLE `BATCH_JOB_EXECUTION` (
`JOB_EXECUTION_ID` bigint NOT NULL,
`VERSION` bigint DEFAULT NULL,
`JOB_INSTANCE_ID` bigint NOT NULL,
`CREATE_TIME` datetime(6) NOT NULL,
`START_TIME` datetime(6) DEFAULT NULL,
`END_TIME` datetime(6) DEFAULT NULL,
`STATUS` varchar(10) DEFAULT NULL,
`EXIT_CODE` varchar(2500) DEFAULT NULL,
`EXIT_MESSAGE` varchar(2500) DEFAULT NULL,
`LAST_UPDATED` datetime(6) DEFAULT NULL,
PRIMARY KEY (`JOB_EXECUTION_ID`),
KEY `JOB_INST_EXEC_FK` (`JOB_INSTANCE_ID`),
CONSTRAINT `JOB_INST_EXEC_FK` FOREIGN KEY (`JOB_INSTANCE_ID`) REFERENCES `BATCH_JOB_INSTANCE` (`JOB_INSTANCE_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `BATCH_JOB_EXECUTION_CONTEXT` (
`JOB_EXECUTION_ID` bigint NOT NULL,
`SHORT_CONTEXT` varchar(2500) NOT NULL,
`SERIALIZED_CONTEXT` text,
PRIMARY KEY (`JOB_EXECUTION_ID`),
CONSTRAINT `JOB_EXEC_CTX_FK` FOREIGN KEY (`JOB_EXECUTION_ID`) REFERENCES `BATCH_JOB_EXECUTION` (`JOB_EXECUTION_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `BATCH_JOB_EXECUTION_PARAMS` (
`JOB_EXECUTION_ID` bigint NOT NULL,
`PARAMETER_NAME` varchar(100) NOT NULL,
`PARAMETER_TYPE` varchar(100) NOT NULL,
`PARAMETER_VALUE` varchar(2500) DEFAULT NULL,
`IDENTIFYING` char(1) NOT NULL,
KEY `JOB_EXEC_PARAMS_FK` (`JOB_EXECUTION_ID`),
CONSTRAINT `JOB_EXEC_PARAMS_FK` FOREIGN KEY (`JOB_EXECUTION_ID`) REFERENCES `BATCH_JOB_EXECUTION` (`JOB_EXECUTION_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `BATCH_JOB_EXECUTION_SEQ` (
`ID` bigint NOT NULL,
`UNIQUE_KEY` char(1) NOT NULL,
UNIQUE KEY `UNIQUE_KEY_UN` (`UNIQUE_KEY`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `BATCH_JOB_INSTANCE` (
`JOB_INSTANCE_ID` bigint NOT NULL,
`VERSION` bigint DEFAULT NULL,
`JOB_NAME` varchar(100) NOT NULL,
`JOB_KEY` varchar(32) NOT NULL,
PRIMARY KEY (`JOB_INSTANCE_ID`),
UNIQUE KEY `JOB_INST_UN` (`JOB_NAME`,`JOB_KEY`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `BATCH_JOB_SEQ` (
`ID` bigint NOT NULL,
`UNIQUE_KEY` char(1) NOT NULL,
UNIQUE KEY `UNIQUE_KEY_UN` (`UNIQUE_KEY`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `BATCH_STEP_EXECUTION` (
`STEP_EXECUTION_ID` bigint NOT NULL,
`VERSION` bigint NOT NULL,
`STEP_NAME` varchar(100) NOT NULL,
`JOB_EXECUTION_ID` bigint NOT NULL,
`CREATE_TIME` datetime(6) NOT NULL,
`START_TIME` datetime(6) DEFAULT NULL,
`END_TIME` datetime(6) DEFAULT NULL,
`STATUS` varchar(10) DEFAULT NULL,
`COMMIT_COUNT` bigint DEFAULT NULL,
`READ_COUNT` bigint DEFAULT NULL,
`FILTER_COUNT` bigint DEFAULT NULL,
`WRITE_COUNT` bigint DEFAULT NULL,
`READ_SKIP_COUNT` bigint DEFAULT NULL,
`WRITE_SKIP_COUNT` bigint DEFAULT NULL,
`PROCESS_SKIP_COUNT` bigint DEFAULT NULL,
`ROLLBACK_COUNT` bigint DEFAULT NULL,
`EXIT_CODE` varchar(2500) DEFAULT NULL,
`EXIT_MESSAGE` varchar(2500) DEFAULT NULL,
`LAST_UPDATED` datetime(6) DEFAULT NULL,
PRIMARY KEY (`STEP_EXECUTION_ID`),
KEY `JOB_EXEC_STEP_FK` (`JOB_EXECUTION_ID`),
CONSTRAINT `JOB_EXEC_STEP_FK` FOREIGN KEY (`JOB_EXECUTION_ID`) REFERENCES `BATCH_JOB_EXECUTION` (`JOB_EXECUTION_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `BATCH_STEP_EXECUTION_CONTEXT` (
`STEP_EXECUTION_ID` bigint NOT NULL,
`SHORT_CONTEXT` varchar(2500) NOT NULL,
`SERIALIZED_CONTEXT` text,
PRIMARY KEY (`STEP_EXECUTION_ID`),
CONSTRAINT `STEP_EXEC_CTX_FK` FOREIGN KEY (`STEP_EXECUTION_ID`) REFERENCES `BATCH_STEP_EXECUTION` (`STEP_EXECUTION_ID`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `BATCH_STEP_EXECUTION_SEQ` (
`ID` bigint NOT NULL,
`UNIQUE_KEY` char(1) NOT NULL,
UNIQUE KEY `UNIQUE_KEY_UN` (`UNIQUE_KEY`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `mail` (
`id` bigint NOT NULL,
`message` varchar(2000) NOT NULL,
`title` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `mail_seq` (
`next_val` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `member` (
`id` bigint NOT NULL,
`email` varchar(255) NOT NULL,
`name` varchar(255) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `member_role` (
`id` bigint NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `member_role_seq` (
`next_val` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
CREATE TABLE `member_seq` (
`next_val` bigint DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_0900_ai_ci;
- ๋ฉ์ธ ํ์ด์ง(/index.html)
- ๋ฉ์ผ์ ๋ฐ์กํ ํ์ ์ ์ฅ(/member)
- ์ ์ฅ๋ ํ์ ์กฐํ(/members)
- ๋๊ท๋ชจ ์ ์กํ ๋ฉ์ผ ์ ๋ชฉ๊ณผ ๋ณธ๋ฌธ ์ ์ฅ(/news-mail)
{
"title": "Spring Batch Mailing Service Test 1",
"text": "### BDD Mailing Service 1์ฐจ ํ
์คํธ\n\n์๋
ํ์ธ์. ๋ถ์ฐ ๊ฐ๋ฐ ๋์๋ฆฌ BDD์
๋๋ค.\n\nBDD ๋ด์ค๋ ํฐ๋ฅผ ์ด์ฉํด์ฃผ์๋ ์ ๋ฐฐ๋๋ค๊ป ์ง์ฌ์ผ๋ก ๊ฐ์ฌ๋๋ฆฝ๋๋ค.\n\n์๋๋ 2024๋
1์ BDD์ ํ๋ ๋ด์ญ๋ค์
๋๋ค.\n\n### ๋ฉ์ธํ์ด์ง ์์ ์์ฑ\n\n\n\n### ํ ํ์ด์ง ๋์์ด๋\n\n\n\n์์ผ๋ก๋ ์ ํฌ BDD๋ฅผ ๋ง์ด ์ฌ๋ํด์ฃผ์ธ์. \n\n๊ฐ์ฌํฉ๋๋ค."
}### BDD Mailing Service 1์ฐจ ํ
์คํธ
์๋
ํ์ธ์. ๋ถ์ฐ ๊ฐ๋ฐ ๋์๋ฆฌ BDD์
๋๋ค.
BDD ๋ด์ค๋ ํฐ๋ฅผ ์ด์ฉํด์ฃผ์๋ ์ ๋ฐฐ๋๋ค๊ป ์ง์ฌ์ผ๋ก ๊ฐ์ฌ๋๋ฆฝ๋๋ค.
์๋๋ 2024๋
1์ BDD์ ํ๋ ๋ด์ญ๋ค์
๋๋ค.
### ๋ฉ์ธํ์ด์ง ์์ ์์ฑ

### ํ ํ์ด์ง ๋์์ด๋

์์ผ๋ก๋ ์ ํฌ BDD๋ฅผ ๋ง์ด ์ฌ๋ํด์ฃผ์ธ์.
๊ฐ์ฌํฉ๋๋ค.- ์ ์ฅ๋ ๋ฉ์ผ๋ค์ ์กฐํ(/news-mails) ํ title ํด๋ฆญ์ ๋ฐ์ก๋ Mail Preview ์ ๊ณต
์ ์ฅ๋ ๋ฉ์ผ๋ค์ ์์ ๊ณผ ์ญ์ ๋ฉ์ผ ๋ฐ์ก๊ธฐ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
- ์ต์ข ๋ฐ์ก๋ ๋ฉ์ผ
- springbatch > bdd > email > entity > MdFormatConvert ์ด๋
- "<img" ํค์๋ ๊ฒ์ (Command + F) 9๋ฒ์งธ <img ํ๊ทธ์ src๋ฅผ ์์
- springbatch > bdd > email > entity > MdFormatConvert ์ด๋
- "<img" ํค์๋ ๊ฒ์ (Command + F) 10, 11, 12 ๋ฒ์งธ <img ํ๊ทธ์ src๋ฅผ ์์
ItemReader Performance
| ItemReader ์ข ๋ฅ | DB ๋ฐ์ดํฐ ๊ฐ์ | ๊ฑธ๋ฆฐ์๊ฐ |
|---|---|---|
| JpaPagingItemReader | 100,000 | 60s |
| JdbcCursorItemReader | 100,000 | 5s |
ItemProcessor Performance
| ItemProcessor ์ข ๋ฅ | DB ๋ฐ์ดํฐ ๊ฐ์ | ๊ฑธ๋ฆฐ ์๊ฐ |
|---|---|---|
| ItemProcessor | 10,000 | 17m 17s |
| AsyncItemProcessor | 10,000 | 3s |
| ๋ฉ์ผ๋ง ์๋น์ค ๋ฒ์ | DB ๋ฐ์ดํฐ ๊ฐ์ | ๊ฑธ๋ฆฐ ์๊ฐ |
|---|---|---|
| v1.0.0 | 10,000 | 12h |
| v2.0.0 | 10,000 | 50s |
Spring-Batch-Bulk-Mailing-Service๋ MIT ๋ผ์ด์ผ์ค์ ๋ฐ๋ผ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์์ธํ ๋ด์ฉ์ LICENSE ํ์ผ์ ์ฐธ์กฐํด์ฃผ์ธ์.
MVP ๋ชจ๋ธ ์ ๋ก๋ ๋ฐ README ์์ฑ
-
๋งํฌ๋ค์ด Heading 3, img ๋งํฌ๋ค์ด ํ๊ทธ๋ฅผ ์ง์ํ๋๋ก ์์
-
์ฌ๋ฌ๊ฐ์ง์ ์ด๋ฏธ์ง๋ฅผ ์ง์ ์ฝ์ ํ ์ ์๋๋ก ์์
- mailItemReader๋ฅผ JpaPagingItemReader์์ JdbcCursorItemReader๋ก ์์ (๋ฐ์ดํฐ 10๋ง๊ฐ ๊ธฐ์ค์ผ๋ก ์ฑ๋ฅ 12๋ฐฐ ์์น)
- ItemProcessor๋ฅผ ๋๊ธฐํ์์ ๋น๋๊ธฐ์ ์ผ๋ก ์ฒ๋ฆฌํ๋๋ก ์์ (๋ฐ์ดํฐ 1๋ง๊ฐ ๊ธฐ์ค์ผ๋ก 340๋ฐฐ ์์น)
- ๋น๋๊ธฐ์ฒ๋ฆฌ ํ ์ค์ ๋ฉ์ผ API ํ ์คํธ ์๋ฃ
- ์์ํ JS ์ฝ๋ ์์






