Spring Batch ๊ธฐ๋ฐ˜ ํšŒ์› ์‚ญ์ œ ๋ฐฐ์น˜ ์‚ฝ์งˆ ๊ธฐ๋ก

2024. 9. 3. 04:08ยท๐Ÿ“‚ backend/spring
ํ† ์ด ํ”„๋กœ์ ํŠธ์—์„œ JPA ๊ธฐ๋ฐ˜ ํšŒ์› ์‚ญ์ œ ๋ฐฐ์น˜๋ฅผ ๊ฐœ๋ฐœํ•˜๋ฉด์„œ ๊ฒช์—ˆ๋˜ ์Šคํ† ๋ฆฌ์— ๋Œ€ํ•ด ์ •๋ฆฌํ•˜๊ณ ์ž ์ž‘์„ฑํ•œ ๋‚ด์šฉ

 

1. Background

(1) JPA ์—ฐ๊ด€ ๊ด€๊ณ„

ํšŒ์› ์‚ญ์ œ ๋ฐฐ์น˜๋Š” soft delete ๋ฅผ ํ†ตํ•ด ํƒˆํ‡ดํ•œ ํšŒ์›์˜ ์‚ฌ์šฉ์ž ์ •๋ณด๋ฅผ ํŠน์ • ๊ธฐ๊ฐ„๋™์•ˆ ๋ฐ์ดํ„ฐ๋ฅผ ๋ณด๊ด€ํ•œ ๋’ค ์ œ๊ฑฐํ•˜๋Š” ๋ฐฐ์น˜์ด๋‹ค. DB ๋ฐ์ดํ„ฐ๋Š” JPA ๊ธฐ๋ฐ˜์œผ๋กœ ์ œ์–ดํ•˜๊ณ  ์žˆ๋‹ค.

 

ํšŒ์› ์‚ญ์ œ ๋ฐฐ์น˜๋Š” Member ์™€ MemberRoleMapping ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐํ•˜๋Š” ๊ณผ์ •์ด๋ฉฐ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ๋งบ๊ณ  ์žˆ์—ˆ๋‹ค. ๋˜ํ•œ, Member entity ์ œ๊ฑฐํ•  ๋•Œ ํ•จ๊ป˜ MemberRoleMapping ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด  cascade option ์ด ์„ค์ •๋˜์–ด ์žˆ๋‹ค.

 

// Member Entity
@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@EntityListeners(AuditingEntityListener.class)
public class Member {

	@Id
	@UnixTimeOrderedUuidGeneratedValue
	@Column(columnDefinition = "BINARY(16)")
	private UUID id;
    
	@OneToOne(
		fetch = FetchType.LAZY,
		mappedBy = "member",
		orphanRemoval = true)
	private MemberRoleMapping memberRoleMapping;
	
    // ...
}

// MemberRoleMapping Entity
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class MemberRoleMapping {

	@Id
	@GeneratedValue(strategy = GenerationType.IDENTITY)
	private Long id;

	@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
	@Setter
	private Member member;
    
    // ...
}

 

 

2. Delete Step ์ฟผ๋ฆฌ ๊ณ ๋ คํ•˜๊ธฐ

(1) ์—ฐ๊ด€ ๊ด€๊ณ„์—์„œ Delete query ์—์„œ N + 1 Query ์ œ๊ฑฐํ•˜๊ธฐ

1์ฐจ ์‹œ๋„๋กœ Member ์™€ MemberRoleMapping ์„ ์ œ๊ฑฐํ•˜๊ธฐ ์œ„ํ•ด ๊ธฐ์กด cascade option ์„ ํ™œ์šฉํ•ด ์—ฐ๊ด€ ๊ด€๊ณ„ ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ ์ž ํ–ˆ๋‹ค. ํ•˜์ง€๋งŒ N + 1 Query ๊ฐ€ ๋ฌธ์ œ์˜€๋‹ค. Member ์˜ delete query ์™ธ์—๋„ MemberRoleMapping select query + MemberRoleMapping delete query ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋งŒ์•ฝ 1.000๊ฑด์˜ ํšŒ์› ๋ฐ์ดํ„ฐ๋ฅผ ์ œ๊ฑฐํ•˜๊ณ ์ž ํ•˜๋Š” ๊ฒฝ์šฐ, 2,000๊ฑด์˜ ์ถ”๊ฐ€ ์ฟผ๋ฆฌ๊ฐ€ ๋ฐœ์ƒํ•œ๋‹ค. ๋ฐฐ๋ณด๋‹ค ๋ฐฐ๊ผฝ์ด ๋” ํฐ ์ƒํ™ฉ์ด๋‹ค. ๐Ÿซ 

  • member delete 1๊ฑด(์ถ”๊ฐ€ : member_role_mapping delete 1๊ฑด + member_role_mapping select 1๊ฑด)

 

memberDeleteWriter

@Bean
@StepScope
public ItemWriter<? super Member> memberDeleteItemWriter() {
    final RepositoryItemsWriter<Member> writer = new RepositoryItemsWriter<>();

    writer.setRepository(memberBatchRepository);
    writer.setMethodName("delete");

    return writer;
}

 

์ถ”๊ฐ€ ์ฟผ๋ฆฌ

// memberRoleMapping ์กฐํšŒ ์ฟผ๋ฆฌ
Hibernate: select mrm1_0.id,mrm1_0.created_date,mrm1_0.member_id,mrm1_0.member_role_id,mrm1_0.modified_date from member_role_mapping mrm1_0 where mrm1_0.member_id=?

// memberRoleMapping ์‚ญ์ œ ์ฟผ๋ฆฌ
Hibernate: delete from member_role_mapping where id=?

// member ์‚ญ์ œ ์ฟผ๋ฆฌ
Hibernate: delete from member where id=?

 

 

(2) RepositoryItemWriter ๋Š” IN ์ ˆ delete ๋ถˆ๊ฐ€๋Šฅ.  CustomRepositoryItemWriter ๋งŒ๋“ค๊ธฐ

N+1 ์ฟผ๋ฆฌ ์ด์Šˆ๋ฅผ ๊ฐœ์„ ํ•˜๊ณ ์ž ์‚ญ์ œ ๋ฐฐ์น˜์—์„œ๋Š” ์—ฐ๊ด€ ๊ด€๊ณ„ ํ™œ์šฉํ•˜๋Š” ๋Œ€์‹  memberId ๋ฅผ IN ์ ˆ์„ ๊ธฐ๋ฐ˜ํ•œ delete query ๋กœ ๋ณ€๊ฒฝํ•˜๊ณ ์ž ํ–ˆ๋‹ค. delete query ๋ฅผ ์ˆ˜ํ–‰ํ•  ๋•Œ๋„ index ๋ฅผ ํƒ€๊ธฐ ๋•Œ๋ฌธ์— FK ๋˜๋Š” PK ๋ฅผ ํ™œ์šฉํ•˜๊ณ  ์‹ถ์—ˆ๋‹ค.

 

ํ•˜์ง€๋งŒ, ํ•œ๊ฐ€์ง€ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. RepositoryItemReader ๋Š” IN ์ ˆ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์—†๋‹ค.

 

๋‚ด๋ถ€ ์ฝ”๋“œ๋ฅผ ์‚ดํŽด๋ณด์ž. RepositoryItemReader ๋Š” ๋ฆฌํ”Œ๋ ‰์…˜์„ ๊ธฐ๋ฐ˜์œผ๋กœ chunk ๋‹จ์œ„์˜ ๋ฐ์ดํ„ฐ๋ฅผ ์ผ์ผ์ด ์ˆœํšŒํ•˜์—ฌ ์ฟผ๋ฆฌ๋ฅผ ์‹คํ–‰ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ ๋™์ž‘ํ•˜๊ธฐ ๋•Œ๋ฌธ์— IN ์ ˆ์„ ํ™œ์šฉํ•  ์ˆ˜ ์—†๋‹ค. ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ List type ์œผ๋กœ ์ถ”๊ฐ€ํ•˜์—ฌ ์‹คํ–‰ํ•  ๊ฒฝ์šฐ, reflection ์„ ํ†ตํ•ด target method ์— ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์€ argument ๊ฐ€ ์ฃผ์ž…๋œ ๊ฒƒ์œผ๋กœ ํŒ๋‹จํ•ด NoSuchMethodException ๋ฅผ ๋ฐ˜ํ™˜ํ•œ๋‹ค.

RepositoryItemWriter.doWrite()

 

 

 

 

 

ํ•˜์ง€๋งŒ CustomRepositoryItemWriter ํด๋ž˜์Šค๋ฅผ ๋งŒ๋“ค์–ด์„œ IN์ ˆ์„ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค. chunk ์˜ ์•„์ดํ…œ๋“ค์„ ์ˆœํšŒํ•˜๋Š” ๋กœ์ง์„ ๊ทธ๋Œ€๋กœ argument ๋กœ ์ฃผ์ž…ํ•˜๋Š” CustomRepositoryItemWriter ๋ฅผ ์ƒ์„ฑํ•˜๋ฉด List ํƒ€์ž…์˜ ํŒŒ๋ผ๋ฏธํ„ฐ ๋ฉ”์„œ๋“œ๋ฅผ ํ˜ธ์ถœํ•  ์ˆ˜ ์žˆ๋‹ค.

๋‚ด๊ฐ€ ๋งŒ๋“  ํด๋ž˜์Šค ๋‚ด๋ถ€ ์ฝ”๋“œ

 

 

 

 

 

CustomRepositoryItemWriter ๋งŒ๋“ค์–ด๋ณด์ž ๐Ÿ‘Š

 

CustomRepositoryItemWriter ๋ฅผ ํ†ตํ•ด IN ์ ˆ์— java.util.List ๊ธฐ๋ฐ˜์˜ argument ๋ฅผ ์ „๋‹ฌ ๊ฐ€๋Šฅํ•˜๋„๋ก ๋งŒ๋“ค๊ณ  JPQL ๊ธฐ๋ฐ˜์˜ delete custom query ๋ฅผ ์ž‘์„ฑํ•ด ๋“ฑ๋กํ–ˆ๋‹ค. ์ฟผ๋ฆฌ๋ฅผ ํ™•์ธํ•ด๋ณด๋ฉด ๊ธฐ์กด์˜ N + 1 ์ฟผ๋ฆฌ๋Š” ๋ฐœ์ƒํ•˜์ง€ ์•Š๊ณ  IN ์ ˆ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋‘ ์—”ํ‹ฐํ‹ฐ ๋ชจ๋‘ ์‚ญ์ œํ•˜๋Š” ๊ฒƒ์„ ํ™•์ธํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค. (Member delete query ๋Š” JpaRepository ์—์„œ ์ œ๊ณตํ•˜๋Š” deleteAllByIdInBatch ๋ฅผ ์‚ฌ์šฉํ–ˆ๋‹ค.)

 

CustomRepositoryItemWriter

// memberDeleteItemWriter
@Bean
@StepScope
public ItemWriter<? super Member> memberDeleteItemWriter() {
    final CustomRepositoryItemWriter<Member> writer = new CustomRepositoryItemWriter<>();

    writer.setRepository(memberBatchRepository);
    writer.setMethodName("deleteMemberRoleMappingByMemberIds");

    return writer;
}

// MemberBatchRepository
public interface MemberBatchRepository extends JpaRepository<Member, UUID> {
	@Modifying
	@Query("DELETE FROM MemberRoleMapping mrm WHERE mrm.member.id IN :memberIds")
	void deleteMemberRoleMappingByMemberIds(@Param("memberIds") Iterable<UUID> memberIds);
}

 

 

์‚ญ์ œ ์ฟผ๋ฆฌ

// member_role_mapping ์‚ญ์ œ ์ฟผ๋ฆฌ
Hibernate: delete mrm1_0 from member_role_mapping mrm1_0 where mrm1_0.id in (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)



// member ์‚ญ์ œ ์ฟผ๋ฆฌ
Hibernate: delete m1_0 from member m1_0 where m1_0.id in (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)

 

 

 

 

 

(3) membeRoleMapping + member ์‚ญ์ œ ์ฟผ๋ฆฌ: ํŠธ๋žœ์žญ์…˜ ๋ฌถ๊ธฐ

Spring Batch ์—์„œ๋Š” Step level ์—์„œ TransactionManager ๋ฅผ ์„ ์–ธํ•ด ํŠธ๋žœ์žญ์…˜์„ ์ง€์›ํ•œ๋‹ค. ๊ทธ๋Ÿฌ๋ฏ€๋กœ Step level ์—์„œ ํŠธ๋žœ์žญ์…˜์„ ๋ฌถ์–ด์•ผ ํ•œ๋‹ค. ๊ธฐ๋ณธ Step ๊ตฌ์กฐ๋Š” reader - proccessor - writer ๋ฅผ ๊ฐ๊ฐ ํ•˜๋‚˜์”ฉ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๋‹ค.

 

ํ˜„์žฌ Step ๊ตฌ์„ฑ์€ ์‚ญ์ œ ํšŒ์› ๋ชฉ๋ก ์กฐํšŒ(reader) - ํšŒ์› ๊ถŒํ•œ ๋งคํ•‘ ์‚ญ์ œ(writer) - ํšŒ์› ์‚ญ์ œ(writer) ์ด๋ฏ€๋กœ ๋ถˆ๊ฐ€๋Šฅํ•ด๋ณด์ด์ง€๋งŒ CompositeItemWriter ๋ฅผ ํ™œ์šฉํ•˜๋ฉด reader ์˜ ์กฐํšŒ ๋ฐ์ดํ„ฐ๋ฅผ ํ†ตํ•ด ์—ฌ๋ ค Writer ๋ฅผ ์„ ์–ธํ•  ์ˆ˜ ์žˆ๋‹ค.

@Bean
public Step memberDeletionStep(JobRepository jobRepository) {
    log.info("memberDeleteStep started.");
    return new StepBuilder("memberDeleteStep", jobRepository)
        .<UUID, Member>chunk(100, transactionManager)
        .reader(memberDeleteItemReader())
        .writer(memberDeleteCompositeWriter())
        .build();
}

@Bean
@StepScope
public ItemWriter<? super Member> memberDeleteCompositeWriter() {
    log.debug("memberDeleteCompositeWriter started.");
    return new CompositeItemWriter<>(List.of(
        memberRoleDeleteItemWriter(),
        memberDeleteItemWriter()));
}

 

 

 

 

 

3.  ์‹คํ–‰ ๊ณ„ํš ํ™•์ธ

์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ 50๊ฑด์„ ๊ธฐ์ค€์œผ๋กœ ์‹คํ–‰ ๊ณ„ํš์„ ํ™•์ธํ•ด๋ณด์•˜๋‹ค. memberId ๋ฅผ FK ๋กœ ์„ ์–ธ์œผ๋กœ ์ธํ•ด ์ƒ์„ฑ๋œ index ์ธ member_id ๋ฅผ ํ†ตํ•ด์„œ index range scan ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•œ ๊ฒƒ์„ ํ™•์ธํ•ด๋ณผ ์ˆ˜ ์žˆ๋‹ค.

explain delete from member_role_mapping in member_id IN (?, ?, ?);

+--+-----------+-------------------+----------+-----+-------------+---------+-------+-----+----+--------+-----------+
|id|select_type|table              |partitions|type |possible_keys|key      |key_len|ref  |rows|filtered|Extra      |
+--+-----------+-------------------+----------+-----+-------------+---------+-------+-----+----+--------+-----------+
|1 |DELETE     |member_role_mapping|null      |range|member_id    |member_id|17     |const|1   |100     |Using where|
+--+-----------+-------------------+----------+-----+-------------+---------+-------+-----+----+--------+-----------+
show index from member_role_mapping

+-------------------+----------+--------------+------------+--------------+---------+-----------+--------+------+----+----------+-------+-------------+-------+----------+
|Table              |Non_unique|Key_name      |Seq_in_index|Column_name   |Collation|Cardinality|Sub_part|Packed|Null|Index_type|Comment|Index_comment|Visible|Expression|
+-------------------+----------+--------------+------------+--------------+---------+-----------+--------+------+----+----------+-------+-------------+-------+----------+
|member_role_mapping|0         |PRIMARY       |1           |id            |A        |1          |null    |null  |    |BTREE     |       |             |YES    |null      |
|member_role_mapping|1         |member_id     |1           |member_id     |A        |1          |null    |null  |YES |BTREE     |       |             |YES    |null      |
|member_role_mapping|1         |member_role_id|1           |member_role_id|A        |1          |null    |null  |YES |BTREE     |       |             |YES    |null      |
+-------------------+----------+--------------+------------+--------------+---------+-----------+--------+------+----+----------+-------+-------------+-------+----------+

 

 

 

 

 

4.  ๋ฐฐ์น˜ ์‹คํ–‰ ์‹œ๊ฐ„ ํ™•์ธ & ํ›„๊ธฐ

50๊ฑด์˜ ์ƒ˜ํ”Œ ๋ฐ์ดํ„ฐ๋ฅผ ํ† ๋Œ€๋กœ Step์„ ๋งŒ๋“ค์–ด ๋น„๊ตํ•ด๋ณด์ž.

RepositoryItemWriter ๊ธฐ๋ฐ˜์„ ๋Œ€์กฐ๊ตฐ์œผ๋กœ ๋น„๊ตํ–ˆ์„ ๋•Œ, CustomRepositoryItemWriter ์˜ ์‹คํ–‰ ์‹œ๊ฐ„์ด 1/3 ์œผ๋กœ ์ค„์–ด๋“ค์—ˆ๋‹ค.

 

# N + 1 ๊ธฐ๋ฐ˜ delete ๋ฐฐ์น˜ ์‹คํ–‰ ์‹œ๊ฐ„
Step: [memberDeleteStep] executed in 654ms

# IN์ ˆ delete ๋ฐฐ์น˜ ์‹คํ–‰ ์‹œ๊ฐ„
Step: [memberDeleteStep] executed in 192ms

 

 

JPA ๋ฅผ ๊ธฐ๋ฐ˜ Spring Batch Job ์„ ์ž‘์„ฑํ•˜๋ฉฐ ๋А๋‚€์ 

  • Entity ์˜ ์—ฐ๊ด€ ๊ด€๊ณ„๋Š” key๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ๊ฐ„์ ‘ ์ฐธ์กฐ๋ฅผ ํ™œ์šฉํ•˜๊ณ  ์ตœ๋Œ€ํ•œ ์ง€์—ฐํ•ด์„œ ์—ฐ๊ด€ ๊ด€๊ณ„๋ฅผ ์„ค์ •ํ•˜๋Š” ๊ฒƒ์ด ์ ํ•ฉํ•œ ๊ฒƒ ๊ฐ™๋‹ค. ์—ฐ๊ด€ ๊ด€๊ณ„ ์„ค์ • ์‹œ, ๊ณ ๋ ค ์‚ฌํ•ญ๊ณผ ์ œ์•ฝ ์‚ฌํ•ญ์ด ๋งŽ์•„์ ธ ๋ณ€๊ฒฝ์ด ์–ด๋ ค์›Œ์งˆ ์ˆ˜ ์žˆ๋‹ค. (e.g. N + 1 ์ฟผ๋ฆฌ, cascade option)
  • ๋ฐฐ์น˜ ์ž‘์—…์—์„œ rows deletion ์€ IN ์ ˆ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ๋™์ž‘ํ•˜๋„๋ก ๊ตฌ์„ฑ์„ ์šฐ์„  ์ˆœ์œ„๋กœ ๊ณ ๋ คํ•˜์ž. ๋‹น์—ฐํ•œ ์ด์•ผ๊ธฐ์ง€๋งŒ ๋ถ€๊ฐ€ ์ฟผ๋ฆฌ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ฑ๋Šฅ์— ์˜ํ–ฅ์„ ๋ฏธ์น  ์ˆ˜ ์žˆ๋‹ค.

 

'๐Ÿ“‚ backend > spring' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€

MessageSourceAutoConfiguration ์ฝ”๋“œ ๊ฒ€ํ† ํ•˜๊ธฐ  (0) 2024.11.28
[kurly tech blog] Redisson, Spring AOP ๊ธฐ๋ฐ˜ ๋ถ„์‚ฐ๋ฝ ์ ์šฉ ๋ฐฉ๋ฒ• summary  (0) 2024.11.26
[Spring Batch] Job ์‹คํ–‰ ํ”„๋กœํผํ‹ฐ, JobParameter, Scope  (0) 2024.08.26
Spring Batch Architecture  (0) 2024.05.10
hibernate.query.in_clause_parameter_padding = true ?  (1) 2024.03.06
'๐Ÿ“‚ backend/spring' ์นดํ…Œ๊ณ ๋ฆฌ์˜ ๋‹ค๋ฅธ ๊ธ€
  • MessageSourceAutoConfiguration ์ฝ”๋“œ ๊ฒ€ํ† ํ•˜๊ธฐ
  • [kurly tech blog] Redisson, Spring AOP ๊ธฐ๋ฐ˜ ๋ถ„์‚ฐ๋ฝ ์ ์šฉ ๋ฐฉ๋ฒ• summary
  • [Spring Batch] Job ์‹คํ–‰ ํ”„๋กœํผํ‹ฐ, JobParameter, Scope
  • Spring Batch Architecture
cooper25
cooper25
  • cooper25
    dev cooper
    cooper25
  • ์ „์ฒด
    ์˜ค๋Š˜
    ์–ด์ œ
    • ๋ถ„๋ฅ˜ ์ „์ฒด๋ณด๊ธฐ (84)
      • ๐Ÿ“‚ backend (34)
        • spring (19)
        • architecture (10)
        • test (5)
      • ๐Ÿ“‚ computer-science (5)
      • ๐Ÿ“‚ programming-language (12)
        • java (12)
      • ๐Ÿ“‚ infra (9)
        • mysql (4)
        • redis (2)
        • message-queue (3)
      • ๐Ÿ“‚ cloud (2)
        • aws (2)
      • ๐Ÿ“‚ frontend (1)
        • react (0)
      • ๐Ÿ“‚ education & lecture (16)
        • ์ธํ”„๋ผ ๊ณต๋ฐฉ (11)
        • ํ•ญํ”Œ ๋ฐฑ์—”๋“œ 7๊ธฐ (5)
      • ๐Ÿ“‚ ai (1)
        • claude (1)
      • ๐Ÿ“‚ etc (2)
        • ํšŒ๊ณ  (1)
        • ์ปจํผ๋Ÿฐ์Šค (1)
  • ๋ธ”๋กœ๊ทธ ๋ฉ”๋‰ด

    • ํ™ˆ
    • ํƒœ๊ทธ
    • ๋ฐฉ๋ช…๋ก
  • ๋งํฌ

  • ๊ณต์ง€์‚ฌํ•ญ

  • ์ธ๊ธฐ ๊ธ€

  • ํƒœ๊ทธ

    ์Šคํ”„๋ง์บ ํ”„ ํ›„๊ธฐ
    spring AOP
    ๋™์‹œ์„ฑ
    react
    Redisson
    spring
    ์ธํ”„๋ผ๊ณต๋ฐฉ
    ๊ฐ€์ƒ ๋ฉด์ ‘ ์‚ฌ๋ก€๋กœ ๋ฐฐ์šฐ๋Š” ๋Œ€๊ทœ๋ชจ ์„ค๊ณ„
    UUID
    ํ•ญํ•ดํ”Œ๋Ÿฌ์Šค
    kafka
    ๋ฐ์ดํ„ฐ ์ค‘์‹ฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์„ค๊ณ„
    AWS
    gof
    ํšŒ๊ณ 
    mysql
    spring camp 2025
    spring-batch
    JPA
    nGrinder
  • ์ตœ๊ทผ ๋Œ“๊ธ€

  • ์ตœ๊ทผ ๊ธ€

  • hELLOยท Designed By์ •์ƒ์šฐ.v4.10.6
cooper25
Spring Batch ๊ธฐ๋ฐ˜ ํšŒ์› ์‚ญ์ œ ๋ฐฐ์น˜ ์‚ฝ์งˆ ๊ธฐ๋ก
์ƒ๋‹จ์œผ๋กœ

ํ‹ฐ์Šคํ† ๋ฆฌํˆด๋ฐ”