0502 ~ 0515


# 0502 ~ 0515

# 0502 - fetch join vs join

# ์ผ๋ฐ˜ Join

  • Fetch Join๊ณผ ๋‹ฌ๋ฆฌ ์—ฐ๊ด€ Entity์— Join์„ ๊ฑธ์–ด๋„ ์‹ค์ œ ์ฟผ๋ฆฌ์—์„œ SELECT ํ•˜๋Š” Entity๋Š” ์˜ค์ง JPQL์—์„œ ์กฐํšŒํ•˜๋Š” ์ฃผ์ฒด๊ฐ€ ๋˜๋Š” Entity๋งŒ ์กฐํšŒํ•˜์—ฌ ์˜์†ํ™”.
  • ์กฐํšŒ์˜ ์ฃผ์ฒด๊ฐ€ ๋˜๋Š” Entity๋งŒ SELECTํ•ด์„œ ์˜์†ํ™”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ๋ฐ์ดํ„ฐ๋Š” ํ•„์š”ํ•˜์ง€ ์•Š์ง€๋งŒ ์—ฐ๊ด€ Entity๊ฐ€ ๊ฒ€์ƒ‰์กฐ๊ฑด์—๋Š” ํ•„์š”ํ•œ ๊ฒฝ์šฐ์— ์ฃผ๋กœ ์‚ฌ์šฉ๋œ๋‹ค.

# Fetch Join

  • ์กฐํšŒ์˜ ์ฃผ์ฒด๊ฐ€ ๋˜๋Š” Entity ์ด์™ธ์— Fetch Join์ด ๊ฑธ๋ฆฐ ์—ฐ๊ด€ Entity๋„ ํ•จ๊ป˜ SELECTํ•˜์—ฌ ๋ชจ๋‘ ์˜์†ํ™”.
  • Fetch Join์ด ๊ฑธ๋ฆฐ Entity ๋ชจ๋‘ ์˜์†ํ™”ํ•˜๊ธฐ ๋•Œ๋ฌธ์— FetchType์ด Lazy์ธ Entity๋ฅผ ์ฐธ์กฐํ•˜๋”๋ผ๋„ ์ด๋ฏธ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์— ๋“ค์–ด์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๋”ฐ๋กœ ์ฟผ๋ฆฌ๊ฐ€ ์‹คํ–‰๋˜์ง€ ์•Š์€ ์ฑ„๋กœ N+1๋ฌธ์ œ๊ฐ€ ํ•ด๊ฒฐ๋œ๋‹ค.

# 0503 - OAtuh๊ด€๋ จ

OAuth๋Š” ์ธํ„ฐ๋„ท ์‚ฌ์šฉ์ž๋“ค์ด ๋น„๋ฐ€๋ฒˆํ˜ธ๋ฅผ ์ œ๊ณตํ•˜์ง€ ์•Š๊ณ  ๋‹ค๋ฅธ ์›น์‚ฌ์ดํŠธ ์ƒ์˜ ์ž์‹ ๋“ค์˜ ์ •๋ณด์— ๋Œ€ํ•ด ์›น์‚ฌ์ดํŠธ๋‚˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์ ‘๊ทผ ๊ถŒํ•œ์„ ๋ถ€์—ฌ ํ•  ์ˆ˜ ์žˆ๋Š” ๊ณตํ†ต์ ์ธ ์ˆ˜๋‹จ์œผ๋กœ์„œ ์‚ฌ์šฉ๋˜๋Š”, ์ ‘๊ทผ ์œ„์ž„์„ ์œ„ํ•œ ๊ฐœ๋ฐฉํ˜• ํ‘œ์ค€์ด๋‹ค. ์‰ฝ๊ฒŒ๋งํ•ด, ์ž์‹ ์ด ์†Œ์œ ํ•œ ๋ฆฌ์†Œ์Šค์— ์†Œํ”„ํŠธ์›จ์–ด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๋„๋ก ํ—ˆ์šฉํ•ด ์คŒ์œผ๋กœ์จ ์ ‘๊ทผ ๊ถŒํ•œ์„ ์œ„์ž„ํ•ด์ฃผ๋Š” ๊ฐœ๋ฐฉํ˜• ํ‘œ์ค€ ํ”„๋กœํ† ์ฝœ

์šฉ์–ด

  • Resource Server : OAuth2.0 ์„œ๋น„์Šค๋ฅผ ์ œ๊ณตํ•˜๊ณ  ์ž์›์„ ๊ด€๋ฆฌํ•˜๋Š” ์„œ๋ฒ„
  • Resource Owner : Resource Server์˜ ๊ณ„์ •์„ ์†Œ์œ ํ•˜๊ณ  ์žˆ๋Š” ์‚ฌ์šฉ์ž
  • Client : Resource Server์˜ API๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฅผ ๊ฐ€์ ธ์˜ค๋ ค๊ณ  ํ•˜๋Š” ์‚ฌ์ดํŠธ
  • Authorization Server : Client๊ฐ€ Resource Server์˜ ์„œ๋น„์Šค๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ์ธ์ฆํ•˜๊ณ  ํ† ํฐ์„ ๋ฐœ์ƒํ•ด์ฃผ๋Š” ์„œ๋ฒ„
  • Access Token : ์ž์› ์„œ๋ฒ„์— ์ž์›์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” ํ† ํฐ
  • Refresh Token : ๊ถŒํ•œ ์„œ๋ฒ„์— ์ ‘๊ทผ ํ† ํฐ์„ ์š”์ฒญํ•  ์ˆ˜ ์žˆ๋Š” ํ† ํฐ

OAuth Bearer token๊ณผ JWTํ† ํฐ์˜ ์ฐจ์ด
OAuth Token์€ ์–ด๋–ค ์‚ฌ์šฉ์ž์˜ ์ •๋ณด์™€ ๊ฐ™์€ ์ค‘์š”ํ•œ ์ •๋ณด๊ฐ€ ์žˆ๋Š” ํ† ํฐ์€ ์•„๋‹ˆ๋ฉฐ, ๋ฆฌ๋กœ์Šค ์„œ๋ฒ„์—์„œ ์ •๋ณด๋ฅผ ์š”์ฒญํ•  ์šฉ๋„๋กœ ์‚ฌ์šฉ. JWT๋Š” payload์— ๋ช…ํ™•ํ•œ ์ •๋ณด๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์œผ๋ฉฐ ํ—ค๋”, ๋‚ด์šฉ, ์„œ๋ช… ๊ตฌ์กฐ๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ๋‹ค.


# 0504 - @GeneratedValue ์ „๋žต

๊ธฐ๋ณธํ‚ค๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•˜๊ธฐ ์œ„ํ•ด ์‚ฌ์šฉ

# IDENTITY

@GeneratedValue(strategy = GenerationType.IDENTITY)
  • ๊ธฐ๋ณธํ‚ค ์ƒ์„ฑ์„ ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์—๊ฒŒ ์œ„์ž„ํ•˜๋Š” ๋ฐฉ์‹์œผ๋กœ id๊ฐ’์„ ๋”ฐ๋กœ ํ• ๋‹นํ•˜์ง€ ์•Š์•„๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ž๋™์œผ๋กœ AUTO_INCREMENT๋ฅผ ํ•˜์—ฌ ๊ธฐ๋ณธํ‚ค๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค.

JPA๋Š” ๋ณดํ†ต ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ์—์„œ ๊ฐ์ฒด๋ฅผ ๊ด€๋ฆฌํ•˜๋‹ค๊ฐ€ commit์ด ํ˜ธ์ถœ๋˜๋Š” ์‹œ์ ์— ์ฟผ๋ฆฌ๋ฌธ์„ ์‹คํ–‰ํ•˜๊ฒŒ๋œ๋‹ค. ํ•˜์ง€๋งŒ IDENTITY ์ „๋žต์—์„œ๋Š” EntityManager.persist()๋ฅผ ํ•˜๋Š” ์‹œ์ ์— Insert SQL์„ ์‹คํ–‰ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‹๋ณ„์ž๋ฅผ ์กฐํšŒํ•ด์˜จ๋‹ค.
๊ทธ ์ด์œ ๋Š” ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ๋Š” 1์ฐจ ์บ์‹œ์— PK์™€ ๊ฐ์ฒด๋ฅผ ๊ฐ€์ง€๊ณ  ๊ด€๋ฆฌ๋ฅผ ํ•˜๋Š”๋ฐ ๊ธฐ๋ณธํ‚ค๋ฅผ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—๊ฒŒ ์œ„์ž„ํ–ˆ๊ธฐ ๋•Œ๋ฌธ์— EntityManage.persist() ํ˜ธ์ถœ ํ•˜๋”๋ผ๋„ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๊ฐ’์„ ๋„ฃ๊ธฐ์ „๊นŒ์ง€ ๊ธฐ๋ณธํ‚ค๋ฅผ ๋ชจ๋ฅด๊ณ  ์žˆ๊ธฐ ๋•Œ๋ฌธ์— ๊ด€๋ฆฌ๊ฐ€ ๋˜์ง€ ์•Š๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
๋”ฐ๋ผ์„œ ํŠน๋ณ„ํ•œ ๊ฒฝ์šฐ๋กœ IDENTITY ์ „๋žต์—์„œ๋Š” EntityManage.persist()๋ฅผ ํ•˜๋Š” ์‹œ์ ์— Insert SQL์„ ์‹คํ–‰ํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‹๋ณ„์ž๋ฅผ ์กฐํšŒํ•˜์—ฌ ์˜์†์„ฑ ์ปจํ…์ŠคํŠธ 1์ฐจ ์บ์‹œ์— ๊ฐ’์„ ๋„ฃ์–ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์— ๊ด€๋ฆฌ๊ฐ€ ๊ฐ€๋Šฅํ•ด์ง„๋‹ค.

# SEQUENCE

@Entity
@SequenceGenerator(
    name = "USER_PK_GENERATOR",
    sequenceName = "USER_PK_SEQ",
    initailValue = 1,
    allocationSize = 50
)
public class PkEx() {
    
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE,
                    generator="USER_PK_GENERATOR")
        private Long id;
    
        private String name;
    
}
  • ๋ฐ์ดํ„ฐ ๋ฒ ์ด์Šค์˜ Sequence Object๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ž๋™์œผ๋กœ ๊ธฐ๋ณธํ‚ค๋ฅผ ์ƒ์„ฑํ•ด์ค€๋‹ค.
  • @SequenceGenerator ์–ด๋…ธํ…Œ์ด์…˜์ด ํ•„์š”ํ•˜๋‹ค.

SEQUENCE ์ „๋žต๋„ IDENTITY ์ „๋žต๊ณผ ๋™์ผํ•œ ๋ฌธ์ œ๊ฐ€ ์žˆ๋‹ค. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๊ฐ€ ์ง์ ‘ ๊ธฐ๋ณธํ‚ค๋ฅผ ์ƒ์„ฑํ•ด์ฃผ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
ENtityManage.persist() ๊ฐ€ ํ˜ธ์ถœ ๋˜๊ธฐ ์ „์— ๊ธฐ๋ณธํ‚ค๋ฅผ ๊ฐ€์ ธ์™€์•ผ ํ•˜๋ฏ€๋กœ ํ•˜์ด๋ฒ„๋„ค์ดํŠธ์—์„œ hibernamte:call next value for USER_PK_SEQ์„ ์‹คํ–‰ํ•˜์—ฌ ๊ธฐ๋ณธํ‚ค๋ฅผ ๊ฐ€์ ธ์˜จ๋‹ค.
๊ทธ ํ›„์— EntityManager.persist() ํ˜ธ์ถœํ•˜๊ธฐ ๋•Œ๋ฌธ์— IDENTITY ์ „๋žต๊ณผ ๋‹ค๋ฅด๊ฒŒ ์ฟผ๋ฆฌ๋ฌธ์„ ์‹คํ–‰ํ•˜์ง€ ์•Š๋Š”๋‹ค.
ํ•˜์ง€๋งŒ SEQUENCE ๊ฐ’์„ ๊ณ„์™ DB์—์„œ ๊ฐ€์ ธ์™€์„œ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ ์ €ํ•˜๋ฅผ ์ผ์œผํ‚ฌ ์ˆ˜ ์žˆ๋‹ค.
ํ•ด๊ฒฐ๋ฐฉ๋ฒ•์€ allocationSize์˜ ํฌ๊ธฐ๋ฅผ ์ ๋‹นํžˆ ์„ค์ •ํ•˜์—ฌ ์„ฑ๋Šฅ ์ €ํ•˜ ๊ฐœ์„ ํ•˜๋Š” ๋ฐฉ๋ฒ•์ด ์žˆ๋‹ค.

# TABLE

@Entity
@TableGenerator(
    name = "USER_PK_GENERATOR",
    table = "USER_PK_SEQ",
    pkColumnValue = "USER_SEQ",
    allocationSize = 1
)
public class PkEx() {
    
        @Id
        @GeneratedValue(strategy = GenerationType.SEQUENCE,
                    generator="USER_PK_GENERATOR")
        private Long id;
    
        private String name;
        
}
  • ํ‚ค๋ฅผ ์ƒ์„ฑํ•˜๋Š” ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•˜๋Š” ๋ฐฉ๋ฒ•์œผ๋กœ Sequence์™€ ์œ ์‚ฌํ•˜๋‹ค.
  • @TableGenerator ์–ด๋…ธํ…Œ์ด์…˜์ด ํ•„์š”ํ•˜๋‹ค.

TABLE ์ „๋žต์€ ๋ชจ๋“  ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์—์„œ ์‚ฌ์šฉ์ด ๊ฐ€๋Šฅํ•˜์ง€๋งŒ ์ตœ์ ํ™” ๋˜์–ด์žˆ์ง€ ์•Š์€ ํ…Œ์ด๋ธ”์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ์„ฑ๋Šฅ ์ด์Šˆ๊ฐ€ ์žˆ๋‹ค.

# AUTO

@GeneratedValue(strategy = GenerationType.AUTO)
  • ๊ธฐ๋ณธ ์„ค์ • ๊ฐ’์œผ๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค์— ๋”ฐ๋ผ ๊ธฐ๋ณธํ‚ค๋ฅผ ์ž๋™์œผ๋กœ ์ƒ์„ฑํ•œ๋‹ค.

# ๊ธฐ๋ณธํ‚ค์˜ ์ œ์•ฝ์กฐ๊ฑด

  • null์ด๋ฉด ์•ˆ๋œ๋‹ค.
  • ์œ ์ผํ•˜๊ฒŒ ์‹ค๋ฒฝํ•  ์ˆ˜ ์žˆ์–ด์•ผํ•œ๋‹ค.
  • ๋ณ€ํ•˜์ง€ ์•Š๋Š” ๊ฐ’์ด์–ด์•ผ ํ•œ๋‹ค.

# 0508 - @Retryable

ํŠน์ • Exception์ด ๋ฐœ์ƒํ–ˆ์„ ๊ฒฝ์šฐ ์ผ์ • ํšŸ์ˆ˜๋งŒํผ ์žฌ์‹œ๋„ํ•  ์ˆ˜ ์žˆ๋Š” ์–ด๋…ธํ…Œ์ด์…˜์ด๋‹ค.

  • dependencies ์„ค์ •
implementation 'org.springframework.boot:spring-boot-starter'
implementation 'org.springframework.boot:spring-boot-starter-web:2.5.4'

implementation 'org.springframework.retry:spring-retry:1.3.1'
runtimeOnly 'org.aspectj:aspectjweaver:1.9.7'
  • EnableRetry ์„ค์ •
@SpringBootApplication
@EnableRetry
public class FailsafeRetryApplication {

    public static void main(String[] args) {
        SpringApplication.run(FailsafeRetryApplication.class, args);
    }

}
  • @Retryable๋กœ ์žฌ์‹œ๋„ ์ง„ํ–‰
@Service
public class RetryableService {

    @Retryable(maxAttempts = 2, backoff = @Backoff(2000), value = IllegalStateException.class,
            exclude = { NullPointerException.class, NullPointerException.class })
    public String getRetryable(Integer intValue) {
        ...
    }

    @Recover
    String recover(NullPointerException e) {
        return e.getMessage();
    }

    @Recover
    String recover(NumberFormatException e, Integer intValue) {
        return String.format("%s : %s", e.getMessage(), intValue);
    }
}
- value, include : retryํ•  Exception์„ ์ง€์ •ํ•œ๋‹ค.
- exclude : ์ œ์™ธํ•  Exception์„ ์ง€์ •ํ•œ๋‹ค.
- maxAttempts : ์ตœ๋Œ€ ์žฌ์‹œ๋„ ํšŸ์ˆ˜(default 3)
- backoff : ์žฌ์‹œ๋„ pause ์‹œ๊ฐ„
- @Recover์˜ ๊ฒฝ์šฐ ๋ฐœ์ƒํ•œ Exception์— ๋Œ€ํ•œ return ์ฒ˜๋ฆฌ๋ฅผ ์ง„ํ–‰ํ•  ์ˆ˜ ์žˆ๋‹ค. ๋‹จ, ๋ฆฌํ„ดํƒ€์ž…์€ @Retryable์— ์ •์˜ํ•œ ๋ฆฌํ„ดํƒ€์ž…๊ณผ ๋™์ผํ•ด์•ผ ํ•œ๋‹ค.

# 0511 - isBlank, isEmpty, hasText

  • ์ž๋ฐ” 11๋ฒ„์ „ String.isBlank() : "", null, whitespace ์ฒดํฌ
  • isEmpty() : ", null ์ฒดํฌ (deprecated)
  • StringUtils.hasText() : "", null, whitespace ์ฒดํฌ
Last update: September 13, 2022 21:44
Contributors: ahnjs