๋ง๋ค์๋ ์ฝ๋๋ฅผ ๋ค์ ์ดํด๋ณด๋
๋์์ฑ ๋ฌธ์ ๋ฅผ ์ ๋งคํ๊ฒ ํด๊ฒฐํ๊ณ ์์๋ค.
์ข ๋ ํ์คํ๊ฒ ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํด ๋ณด์.
1. ์์ธ
- ์ด๋ฐ ๋ก์ง
if (customerRepository.existsByCode(code)) {
throw new DuplicateCodeException("์ด๋์ฝ๋๊ฐ ์ค๋ณต๋์์ต๋๋ค.");
}
customerRepository.save(customer);
customerSearchRepository.save(toSearchEntity(customer));
CustomerService ์ค save ๋ฉ์๋์ ์ผ๋ถ๋ถ์ด๋ค. ์ฒ์ ๊ณ ๊ฐ ๋ฑ๋ก ๋ก์ง์ ์ด๋ฐ ํ๋ฆ์ด์๋ค. code๊ฐ ๋ค์ด์ค๋ฉด existsByCode๋ก code๊ฐ ์กด์ฌํ๋์ง ๋ณด๊ณ , ์กด์ฌํ๋ฉด DuplicateCodeException์ ๋์ก๋ค.
๋ ๊ด๋ฆฌ์๊ฐ ๊ฑฐ์ ๋์์ ๊ฐ์ ์ด๋์ฝ๋๋ฅผ ๋ค๊ณ ์์ฒญ์ด ๋ค์ด์๋ค๊ณ ๊ฐ์ ํด ๋ณด์. ์ด๋ฐ ๊ฒฝ์ฐ ๋ฌธ์ ๊ฐ ๋ฐ์ํ ์ ์๋ค.
A: existsByCode("1234") → false B: existsByCode("1234") → falseโ
existsByCode ๋จ๊ณ์์ ์์ง ์ค๋ณต์ด ์๋ค๊ณ ํ๋จ(false)ํ์ฌ A, B ๋ ๋ค ํต๊ณผํ ์ ์๋ ๊ฒ์ด๋ค.
@Table(
name = "customer",
uniqueConstraints = @UniqueConstraint(
name = "uk_customer_code",
columnNames = "code"
)
)
๊ทธ๋ฌ๋ ์ค์ ๋ก ๋ ๋ค ํต๊ณผ๋์ง ์๊ณ ์ ์ฅ๋จ๊ณ์์ ๊ฐ๋ฆด ๊ฒ์ด๋ค.
A: save("1234") ์๋ → ์ฑ๊ณต B: save("1234") ์๋ → ์คํจ OR B: save("1234") ์๋ → ์ฑ๊ณต A: save("1234") ์๋ → ์คํจ
์๋๋ฉด Customer ์ํฐํฐ์ ์์ ์ฝ๋์ฒ๋ผ ์ ๋ํฌ ์ ์ฝ์ ๊ฑธ์ด๋๊ธฐ ๋๋ฌธ์ด๋ค. ๊ทธ๋์ ์ต์ข ์ ์ผ๋ก ๋จผ์ DB์ ๋ฐ์๋ ์์ฒญ์ ์ฑ๊ณตํ๊ณ ๋์ค์ ๊ฐ์ ๊ฐ์ ๋ฃ์ผ๋ ค๋ ์์ฒญ์ ์ ๋ํฌ ์ ์ฝ ์๋ฐ์ผ๋ก ์คํจํ๋ฉด์ ์ค๋ณต ์ ์ฅ์ ์ ๋๋ค.
- ๊ฒฐ๋ก
- ์ ํ๋ฆฌ์ผ์ด์ ๋ ๋ฒจ์ ์ฌ์ ๊ฒ์ฌ(existByCode)๋ ๋์์ฑ ์ํฉ์์ ์์ ํ ๋ณด์ฅ์ด ์๋๋ค.
- ๋คํํ Customer.cdoe ์นผ๋ผ์์ unique ์ ์ฝ์ด ๊ฑธ๋ ค ์์ด์ ์ค์ DB์๋ ๊ฐ์ ์ด๋์ฝ๋๊ฐ ๋ ๋ค ์ ์ฅ๋์ง๋ ์๋๋ค.
- ์ฆ, ์ต์ข ์ ํฉ์ฑ์ DB unique constraint๊ฐ ๋ณด์ฅํ๋ค. - ๋ฌธ์ ์
- unique ์ ์ฝ ์๋ฐ์ด ๋ฐ์ํ์ ๋ ์ฌ์ฉ์์๊ฒ ๋ช ํํ ์๋ต์ ๋ด๋ ค์ฃผ์ง ๋ชปํ๋ค.
@RestController
public class ApiExceptionHandler {
@ExceptionHandler(DuplicateCodeException.class)
public ResponseEntity<String> handleDup(DuplicateCodeException e) {
return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.getMessage());
}
}
์ด ํด๋์ค๊ฐ ์๋ฌ๋ฅผ ์ฒ๋ฆฌํ๋ ํด๋์ค์ธ๋ฐ, ๋ณด๋ฉด BAD_REQUEST๋ก ์ํ์ฝ๋๋ฅผ ์ค์ ํ๋ค. ์ด๊ฑด 400 BadRequest์ด๋ค. 400 BadRequest๋ ๋ณดํต ์์ฒญ์ด ์๋ชป ์์ ๋ ์ฌ์ฉํ๋ค. ์๋ฅผ ๋ค์ด์ ์ด๋ ์ฝ๋๊ฐ 4์๋ฆฌ์ธ๋ฐ, 3์๋ฆฌ๊ฐ ์ค๋ ๊ฒฝ์ฐ ๋ด๋ฆฌ๋ ์๋ต์ด๋ผ๊ณ ๋ณผ ์ ์๋ค.
์ค๋ณต ์ค๋ฅ๋ ๋ณดํต 409 Conflict๋ฅผ ์ฌ์ฉํ๋ค. ๊ทธ๋์ ์ด ์ํ์ ๋ง๊ฒ ์ฝ๋๋ฅผ ์์ ํด์ผ ํ๋ค. ์ฒซ ๋ฒ์งธ๋ก ์ ์ฉ ๋ฒ์๋ฅผ ๋ํ ๊ฒ์ด๋ค. @RestController๋ ํด๋น ์ปจํธ๋กค๋ฌ ๋ด๋ถ์์ ๋ฐ์ํ๋ ์์ธ๋ง ์ก๋๋ฐ, ์ด๊ฑธ ์ ์ญ์ ์ผ๋ก ๊ฐ์งํด์ ์ฒ๋ฆฌํ๋๋ก ํ ๊ฒ์ด๋ค. ๋ ๋ฒ์งธ๋ DB ๋ ๋ฒจ์์ ๋ฐ์ํ๋ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ ์ ์ฝ ์กฐ๊ฑด ์๋ฐ(DataIntegrityViolationException)๊น์ง ํจ๊ป ์ฒ๋ฆฌํ ๊ฑฐ๋ค. ์๋ฌ ๋ฉ์์ง๋ฅผ ํ์ฑ ํด์ ์ด๋ค unique ์ ์ฝ ์กฐ๊ฑด์ ์๋ฐํ๋์ง ๊ตฌ์ฒด์ ์ผ๋ก ํ๋จํ๋ ๋ก์ง์ผ๋ก ์์ ํ ๊ฒ์ด๋ค. ๋ง์ง๋ง์ผ๋ก 400 BadRequset๋ฅผ 409 Conflict๋ก ๋ฐ๊ฟ ๊ฒ์ด๋ค.
- ์ด๋ ์ฝ๋ ๊ฒ์ฆ์ ํ๋ฐํธ์๋์์๋ง ํ๊ณ ์์ด์ ๋ฐฑ์๋ ๊ฒ์ฆ ๋ก์ง๋ ์์ฑํด์ผ ํ๋ค.
String code = customer.getCode();
if (code != null && customerRepository.existsByCode(code)) {
throw new DuplicateCodeException("์ด๋์ฝ๋๊ฐ ์ค๋ณต๋์์ต๋๋ค.");
}
๋๋ ๋ชฐ๋๋ค. ํ๋ก ํธ์๋์์ 4์๋ฆฌ๋ฅผ ๋ง์๋๊ณ ๋ฐฑ์๋์๋ ์์ฑํ์ง ์์๋ค. ํ๋ก ํธ์๋์์ ๋ง์์ผ๋ฉด ๋๋ ๊ฑฐ ์๋๋๊ณ ์๊ฐํ ์ ์๋ค. ๊ทธ๋ฐ๋ฐ ์ด๋ ๊ฒ ์์ฑํ๋ฉด ์ ๋๋ค. ์ ์ด์ ํ๋ฐํธ์๋๋ ๊ฒ์ฆ์ ์ฝ๊ฒ ์ฐํํ ์ ์๋ ๊ตฌ์กฐ์ด๋ค. ๊ทธ๋ ๊ธฐ์ ๋ฐฑ์๋๋ ๋ฐฉ์ด์ ์ค๊ณ๋ฅผ ํด์ผ ํ๋ค. ๋ฐฑ์๋๋ ์ค์ค๋ก ์์ ์ ๋ฐ์ดํฐ ๋ฌด๊ฒฐ์ฑ์ ์งํฌ ์ ์์ด์ผ ํ๋ค.
ํ๋ฐํธ์๋ ๊ฒ์ฆ ๋ก์ง์ UX ๋ชฉ์ ์ด๋ค. ์ฌ์ฉ์๊ฐ ์ค์๋ก ์ด๋ ์ฝ๋ 4์๋ฆฌ ์ค 3์๋ฆฌ๋ง ์ ๋ ฅํ์ ๋, ์๋ฒ๊น์ง ๊ฐ๋ค ์ค๋ ์๊ฐ์ ๋ญ๋นํ์ง ์๊ณ ๋ฐ๋ก 4์๋ฆฌ๋ฅผ ์ ๋ ฅํ๋ผ๊ณ ์๋ ค์ฃผ์ด ๋น ๋ฅธ ์ฌ์ฉ์ ๊ฒฝํ์ ์ ๊ณตํ๋ค. ๋ฐฑ์๋ ๊ฒ์ฆ์ ์ฐํ๋ ๊ณต๊ฒฉ์ด๋ ์๋ชป๋ ๋ฐ์ดํฐ๊ฐ DB ์์คํ ์ ๋ง๊ฐ๋จ๋ฆฌ์ง ์๋๋ก ์ต์ข ์ ์ผ๋ก ๋ฐฉ์ดํ๋ ๋ชฉ์ ์ผ๋ก ์ฌ์ฉํ๋ค.
์ด ์ฝ๋๋ฅผ ๋ณด๋ฉด custoemr ์ํฐํฐ์์ code๋ฅผ ์ง์ ๋ฝ์ ๋ค๊ณ ์์ ํ์ธํ๊ณ ์๋ค. ์ด๋ ๊ฒ ๊ฒ์ฆ์ด ํ์ํ ์นผ๋ผ์ ๊ฒฝ์ฐ ์๋ฒ ๋๋ ํ์ ์ผ๋ก ๋ฐ๋ก ๋นผ์ ๊ฒ์ฆ ๋ก์ง์ ์ง๋ฉด ๋๋ค.
[Spring, React] ์ฝ๋๋ฅผ ์ ๋ ฅํ๋ฉด ๊ณ ๊ฐ ๋ฑ๊ธ์ ๋ง๋ ํ์ด์ง๋ฅผ ๊ฒฐ๊ณผ๋ก ๋ณด๋ด๊ธฐ
VIP ์ด์ ๋ฑ๊ธ์ ๊ณ ๊ฐ์ด์ฝ๋๋ฅผ ์ ๋ ฅํ๋ ํ์ด์ง์ ๋ณธ์ธ ์ฝ๋๋ฅผ ์ ๋ ฅํ๋ฉด๊ทธ์ ๋ง๋ ๋ฑ๊ธ ํ์ด์ง๋ก ์ด๋ํ๋๋ก ๋ง๋ค์ด๋ณด์. 1. ๋์ ํ๋ฉดGIF ์ด๋ฏธ์ง 2. Spring ์ฝ๋ ๋์ ์ค๋ช 1) ์ด๋ ์ฝ๋๋ฅผ ์ ๋ ฅํ๋ฉด
post-this.tistory.com
2. ๋ฆฌํฉํ ๋ง
- InviteCode ํด๋์ค
// JPA์๊ฒ ์ด ํด๋์ค๋ ๋ค๋ฅธ ์ํฐํฐ์ ์ผ๋ถ๋ก ํฌํจ๋ ์ ์๋ ๊ฐ ๊ฐ์ฒด์์ ์๋ฆฐ๋ค.
@Embeddable
// Lombok์ ํตํด ํ๋(value)์ Getter ๋ฉ์๋๋ฅผ ์๋์ผ๋ก ์์ฑํ๋ค.
@Getter
// JPA๊ฐ ๋ด๋ถ์ ์ผ๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํ ๋ ํ์ํ ๊ธฐ๋ณธ ์์ฑ์๋ฅผ ๋ง๋ ๋ค.
// ๋ค๋ง ๋ฌด๋ถ๋ฉธํ ์ธ๋ถ ์์ฑ์ ๋ง๊ธฐ ์ํด PROTECTED๋ก ๋ ๋ฒจ์ ์ค์ ํด์ ์ ๊ทผ์ ์ ์ดํ๋ค.
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class InviteCode {
// ์ฝ๋ ํจํด์ ์ซ์๋ก ์ด๋ค์ง 4์๋ฆฌ์ด๋ค.
private static final String CODE_PATTERN = "\\d{4}";
// ์ด๋์ฝ๋ ๋ฌธ์์ด ๊ฐ์ด ์ ์ฅ๋๋ ์บก์ํ๋(private) ํ๋์ด๋ค.
private String value;
private InviteCode(String value) {
// ์์ฑ ์์ ์ ์
๋ ฅ๊ฐ์ด ์ ํจํ์ง ๊ฒ์ฆํ๋ค.
if (!isValid(value)) {
// ์๋ชป๋์์ผ๋ฉด ํ์ค ์์ธ๋ฅผ ๋์ง๋ค.
throw new IllegalArgumentException("์ด๋์ฝ๋๋ ์ซ์ 4์๋ฆฌ๋ก ์
๋ ฅํด์ผ ํฉ๋๋ค.");
}
// ๊ฒ์ฆ์ด ํต๊ณผ๋๋ฉด trim์ผ๋ก ๊ณต๋ฐฑ์ ์ ๊ฑฐํ ๋ค ํ๋์ ์์ ํ๊ฒ ์ ์ฅํ๋ค.
this.value = value.trim();
}
// ๋ค์ด์จ ์ด๋์ฝ๋๋ฅผ ๊ฐ์ฒด๋ก ๋ง๋๋ ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋์ด๋ค.(of ๋ฉ์๋)
// ์ ์๋ ์ด๋์ฝ๋๋ฅผ ๋จ์ํ String์ผ๋ก ๋ค๋ค์ผ๋ฉด ์ด์ ์ด๋์ฝ๋๋ฅผ ๊ฐ ๊ฐ์ฒด๋ก ๊ฐ์ผ๋ค.
public static InviteCode of(String value) {
// "1234" ๋ฌธ์์ด์ -> InviteCode.of("1234")๊ฐ ๋๋ฉด์ ๊ฐ์ฒด ์์ฑ๋๋ค.
// ๊ทธ๋ฆฌ๊ณ ๋์ ์์ ์๋ InviteCode ์์ฑ์ ์์ผ๋ก ๋ค์ด๊ฐ์ ๊ฒ์ฌํ๋ค.
return new InviteCode(value);
}
// ์ด๋์ฝ๋์ ์ ํจ์ฑ์ ๊ฒ์ฆํ๋ ๋๋ฉ์ธ ๋ก์ง์ด๋ค.
public static boolean isValid(String value) {
// null๋ ์๋๊ณ , ๊ณต๋ฐฑ๋ ์๋๊ณ , ๊ณต๋ฐฑ ์ ๊ฑฐ ํ 4์๋ฆฌ ์ซ์์ฌ์ผํ๋ค.
return value != null && !value.isBlank() && value.trim().matches(CODE_PATTERN);
}
// ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๊ฐ ๋ฌ๋ผ๋ ์์ ๋ฐ์ดํฐ๊ฐ ๊ฐ์ผ๋ฉด ๊ฐ์ ๊ฐ์ฒด๋ก ์ทจ๊ธํ๊ธฐ ์ํ
// ๋๋ฑ์ฑ ๋น๊ต๋ฅผ ์ ์ํ๋ ๋ฉ์๋์ด๋ค.
@Override
public boolean equals(Object o) {
// ๋น๊ต ๋์๊ณผ ๋์ ๋ฉ๋ชจ๋ฆฌ ์ฃผ์๊ฐ ์๋ฒฝํ ๊ฐ๋ค๋ฉด ์ฐธ์ ๋ฐํํ๋ค.
if (this == o)
return true;
// ๋น๊ต ๋์์ด null์ด๊ฑฐ๋ InviteCode ํ์
์ด ์๋๋ผ๋ฉด ๊ฑฐ์ง์ ๋ฐํํ๋ค.
if (!(o instanceof InviteCode that))
return false;
// ์ต์ข
์ ์ผ๋ก ๋ด๋ถ์ value๊ฐ ์๋ก ์ผ์นํ๋์ง ๋น๊ตํ ๊ฒฐ๊ณผ๋ฅผ ๋ฐํํ๋ค.
return Objects.equals(value, that.value);
}
// ๊ฐ์ฒด๋ฅผ ์๋ณํ ์ ์๋ ํด์์ฝ๋๋ฅผ ๋ฐํํ๋ ๋ฉ์๋์ด๋ค.
@Override
public int hashCode() {
// ๋ด๋ถ value ๊ฐ์ ๊ธฐ๋ฐ์ผ๋ก ํด์์ฝ๋ ์์ฑํ๋ค.
// ํด์ ๊ธฐ๋ฐ ์ปฌ๋ ์
์์ ์ค๋ณต ์ฒดํฌ๊ฐ ์ ํํ ์๋ํ๋ค.
return Objects.hash(value);
}
}
์บก์ํ์ ํต์ฌ์ ์ธ๋ถ์์ ๊ฐ์ฒด์ ๋ด๋ถ ๋ฐ์ดํฐ๋ฅผ ๋ง์๋๋ก ์ ๊ทผํ๊ฑฐ๋ ์์ ํ์ง ๋ชปํ๋๋ก ์จ๊ธฐ๊ณ ๊ฐ์ฒด๊ฐ ์ ๊ณตํ๋ ์์ ํ ํต๋ก๋ฅผ ํตํด์๋ง ์ํตํ๋ ๊ฒ์ด๋ค. ์ฌ์ค ์บก์ํ๋ฅผ ํ๋ ค๊ณ ํ ๊ฑด ์๋์๋๋ฐ, ํ๊ณ ๋ณด๋ ์บก์ํ๊ฐ ๋์๋ค(?)
private String value;โ
private๋ก ์ ๊ทผํ์ง ๋ชปํ๋๋ก ๋ง์๊ณ , of( ) ๋ฉ์๋๋ฅผ ํตํด ๊ฒ์ฆ์ ๊ฑฐ์น ๊ฐ๋ง ์ ์ฅํ ์ ์๊ฒ ํ๋ค. -> ์ด๋ฐ ์์ด ์บก์ํ๋ผ๊ณ ํ๋ค.
public static InviteCode of(String value) { return new InviteCode(value); }โ์๋ฐ์์ ํ์ค์ ์ผ๋ก ๊ฐ์ฒด๋ฅผ ์์ฑํ๋ ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ฅผ ๋ง๋ค ๋, of๋ผ๋ ์ด๋ฆ์ ์ฃผ๋ก ์ฌ์ฉํ๋ค. of๊ฐ ์ ํ ๊ฑธ ๋ณด๊ณ ๊ฐ์ฒด๋ฅผ ์์ฑํด ์ฃผ๋ ์ ์ ํฉํ ๋ฆฌ ๋ฉ์๋๋ผ๋ ๊ฑธ ์ ์ ์๋ค.
@Override public boolean equals(Object o) { if (this == o) // ๋ฉ๋ชจ๋ฆฌ ์ฃผ์ ํ์ธ return true; if (!(o instanceof InviteCode that)) // InviteCode ๊ฐ์ฒด์ธ์ง ํ์ธ return false; return Objects.equals(value, that.value); // ์ฝ๋๊ฐ ๊ฐ์๊ฒ ์๋์ง ํ์ธ }
์๋ฐ์ ๋ชจ๋ ํด๋์ค๋ ์ต์์ ํด๋์ค์ธ Object๋ฅผ ์๋์ผ๋ก ์์๋ฐ๋๋ค. equals(Object o)๋ ์ด Object ํด๋์ค๊ฐ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๋ ๋ฉ์๋๋ฅผ ์ฌ์ ์ํ ๊ฒ์ด๋ค. ์ฝ๋์์ Object o๋ ๋น๊ต ๋์์ด ๋ ๋ฉ๋ชจ๋ฆฌ์ ์กด์ฌํ๋ ์ด๋ค ๊ฐ์ฒด๋ฅผ ๋ปํ๋ค. ์๋ฐ์ ๋ชจ๋ ๊ฐ์ฒด๋ Object ํ์ ์ผ๋ก ๋ฐ์ ์ ์๊ธฐ ๋๋ฌธ์ ํ๋ผ๋ฏธํฐ๊ฐ Object o๊ฐ ๋ ๊ฒ์ด๋ค.
this๋ ํ์ฌ ์ด equals ๋ฉ์๋๋ฅผ ์คํํ๊ณ ์๋ ์ฃผ์ฒด ๊ฐ์ฒด๋ฅผ ๋ปํ๋ค. that์ ๋งค๊ฐ๋ณ์๋ก ๋ค์ด์จ Obejct o๋ฅผ InviteCode ํ์ ์ผ๋ก ํ๋ณํํ์ฌ ๊บผ๋ธ ์๋๋ฐฉ ๊ฐ์ฒด๋ฅผ ๋ปํ๋ค.
์ฝ๊ฒ ์ค๋ช ํด ๋ณด๊ฒ ๋ค. ๊ตฌ์คํ ํ์์ด 1234๋ผ๋ ์ด๋์ฝ๋๋ฅผ ๋ค๊ณ ์๋ค. ๊ทธ๋ฌ๋ฉด if(this == o)๋ฅผ ํตํด ์ฃผ์ ๊ฐ์ด ๊ฐ์ ๊ฒ ์๋์ง ํ์ธํ๋ค. ๋ง์ฝ ๊ฐ์ ์ฃผ์๊ฐ์ด ์๋ค๋ฉด ๋ด๋ถ ๋ฌธ์์ด์ ํ์ธํ์ง ์๊ณ ๋ฐ๋ก true๋ฅผ ๋ฐํํ๋ค. ๊ทธ๋ค์ ๋๊ฒจ๋ฐ์ ๊ฐ์ฒด๊ฐ inviteCode ํ์ ์ธ์ง๋ฅผ ํ์ธํ๋ค. String ๋ฌธ์์ด์ ๋ณด๋๋ค๋ฉด ๊ตฌ์กฐ๊ฐ ๋ค๋ฅด๊ธฐ ๋๋ฌธ์ false๋ฅผ ๋ฐํํ๋ค. ์ด์ ๋ง์ง๋ง์ผ๋ก ๊ตฌ์คํ ํ์์ ์ด๋ ์ฝ๋๊ฐ ๋ค๋ฅธ ํ์์ ์ด๋์ฝ๋๊ฐ ๋ง๋ ๊ฒ ์๋ Object.equals๋ก ํ์ธํ๋ค. ๊ธ์๊ฐ ๋๊ฐ์ผ๋ฉด true๋ฅผ ๋ฐํํ๋ค.
[Spring][JPA] @Embeddable์ @Embedded๋ฅผ ์ฌ์ฉํ๋ ๊ฒฝ์ฐ
์ด๋ค ํ๋๋ฅผ ์ผ๋ฐ์ ์ธ ๋ฌธ์์ด๋ก ๋๋ ๊ฒ์ด ์๋์๋ฏธ ์๋ ๊ฐ์ฒด๋ก ๋ง๋ค์ด์ผ ํ๋ ๊ฒฝ์ฐ@Embeddable๊ณผ @Embedded๋ฅผ ๋ถ์ธ๋ค.๋ ์์ธํ๊ฒ ์์๋ณด์. 1. @Embeddable๊ณผ @Embedded@Embeddable์ ๊ฐ ํ์ ์ ์ ์ํ๋ ๊ณณ
post-this.tistory.com
- Customer ํด๋์ค
@Embedded
@AttributeOverride(name = "value", column = @Column(name = "code", nullable = false, length = 4))
private InviteCode inviteCode;
- CustomerService์ save ๋ฉ์๋
if (customerRepository.existsByInviteCode(inviteCode)) {
throw new DuplicateCodeException("์ด๋์ฝ๋๊ฐ ์ค๋ณต๋์์ต๋๋ค.");
}
์ด์ ์๊น ๋ง๋ InviteCode ๋งค๊ฐ๋ณ์๋ฅผ ๋ฃ์ด์ ์ด๋ ์ฝ๋๊ฐ ์๋์ง ํ์ธํ๋ค. ๋งจ ์ฒ์ ์ฝ๋๋ Code๋ฅผ ๋ฝ์์ ์ง์ ๋ฃ์๋ค. ์ด๋ ๊ฒ ๋๋ฉด JPA์ ๊ฐ์ฒด ์ค์ฌ ๊ตฌ์กฐ๋ ์ ๋ง๊ฒ ๋๋ค. ๊ทธ๋์ ํ์ฌ ์ฝ๋์ฒ๋ผ ๋ฐ๊ฟ์คฌ๋ค.
- ApiExceptionHandler ํด๋์ค
// ์ดํ๋ฆฌ์ผ์ด์
๋ด์ ๋ชจ๋ @RestController์์ ๋ฐ์ํ๋ ์์ธ๋ฅผ ์ด๊ณณ์์ ์ฒ๋ฆฌํ๋ค.
@RestControllerAdvice
public class ApiExceptionHandler {
// DuplicateCodeException ์์ธ๋ฅผ ๊ฐ์งํ๋ ๋ฉ์๋์ด๋ค.
@ExceptionHandler(DuplicateCodeException.class)
public ResponseEntity<String> handleDup(DuplicateCodeException e) {
// HTTP ์ํ ์ฝ๋๋ฅผ 409 Conflict๋ก ์ค์ ํ๋ค.
return ResponseEntity.status(HttpStatus.CONFLICT).body(e.getMessage());
}
// DB ๋ ๋ฒจ์์ ์ ์ฝ ์กฐ๊ฑด ์๋ฐ์ผ๋ก ์ธํด ๋ฐ์ํ๋
// DataIntegrityViolationException ์์ธ๋ฅผ ๊ฐ์งํ๋ค.
@ExceptionHandler(DataIntegrityViolationException.class)
public ResponseEntity<String> handleDataIntegrityViolation(DataIntegrityViolationException e) {
// ๋ฐ์ ์์ฑํ ๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ์ด๋์ฝ๋ ์ค๋ณต ๋๋ฌธ์ ์ผ์ด๋๊ฑด์ง ํ์ธํ๋ค.
if (isCustomerCodeUniqueViolation(e)) {
// ์ด๋์ฝ๋ ์ค๋ณต์ด ๋ง์ผ๋ฉด HTTP 409 ์ํ ์ฝ๋์ ํจ๊ป ์๋ฌ ๋ฉ์์ง๋ฅผ ๋ฐํํ๋ค.
return ResponseEntity.status(HttpStatus.CONFLICT).body("์ด๋์ฝ๋๊ฐ ์ค๋ณต๋์์ต๋๋ค.");
}
// ์ด๋์ฝ๋ ์ค๋ณต์ด ์๋ ๋ค๋ฅธ DB ์ ์ฝ ์กฐ๊ฑด ์๋ฐ์ธ ๊ฒฝ์ฐ 409์ ์๋ฌ ๋ฉ์์ง๋ฅผ ๋ฐํํ๋ค.
return ResponseEntity.status(HttpStatus.CONFLICT).body("๋ฐ์ดํฐ ์ค๋ณต ๋๋ ์ ์ฝ ์กฐ๊ฑด ์๋ฐ์ด ๋ฐ์ํ์ต๋๋ค.");
}
// DB ์์ธ์ ์ด์ ๋ฅผ ๋ถ์ํ๋ ๋ฉ์๋์ด๋ค.
private boolean isCustomerCodeUniqueViolation(DataIntegrityViolationException e) {
// ์คํ๋ง ์์ธ ํ๋ ์์ํฌ ๊ธฐ๋ฅ์ผ๋ก ๋ํ๋ ์๋ฌ์์ ๊ฐ์ฅ ๊ตฌ์ฒด์ ์ธ ์๋ฌ๋ฅผ ๊บผ๋ด์จ๋ค.
Throwable cause = e.getMostSpecificCause();
// ์ผํญ ์ฐ์ฌ์๋ก ์ตํ๋น ์๋ฌ๊ฐ ์์ผ๋ฉด ๊ฐ๊ณ ์ค๊ณ ์์ผ๋ฉด ์์ ์๋ฌ e๋ฅผ ์ฌ์ฉํ๋ค.
String message = cause == null ? e.getMessage() : cause.getMessage();
// "uk_customer_code"๊ฐ ํฌํจ๋์ด ์๋์ง๋ฅผ true, false๋ก ๋ฐํํ๋ค.
return message != null && message.toLowerCase().contains("uk_customer_code");
}
}
3. ํด๊ฒฐ?
- ๋์์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํ๋ ๋ฐฉ๋ฒ์ ๋ํ์ ์ผ๋ก 3๊ฐ์ง ์ ๋๊ฐ ์๋ค.
- ์ฒซ ๋ฒ์งธ๋ ์ง๊ธ ํ ๋ฐฉ๋ฒ์ธ ๋ฐ์ดํฐ๋ฒ ์ด์ค Unique ์ ์ฝ ์กฐ๊ฑด์ด๋ค.
- ๋ ๋ฒ์งธ๋ ๋ฐ์ดํฐ๋ฒ ์ด์ค ๋น๊ด์ ๋ฝ์ด๋ค. existByCode๋ฅผ ์ฌ์ฉํ ๋ ๋ฐ์ดํฐ๊ฐ ์์ผ๋ฉด ์ฝ๊ธฐ ์ ๊ธ์ ๊ฑธ๊ฑฐ๋, ๊ณต์ ์์์ ๋๊ธฐํ๋๋ก ๋ง๋๋ ๋ฐฉ๋ฒ์ด๋ค. ๊ทธ๋ฐ๋ฐ ๋น๊ด์ ๋ฝ์ ํ์ฌ ํ๋ก์ ํธ์ ๋ง์ง ์๋๋ค. ์๋๋ฉด ๋์์ ๊ฐ์ ์ด๋์ฝ๋๊ฐ ๋ค์ด์ค๋ฉด Insert ๋๊ธฐ ์ ์ด๋ผ์ ๋ฝ์ ๊ฑธ ์ ์๊ฒ ๋๋ค. ๊ทธ๋์ ๊ฒฐ๊ตญ ๋์ผํ ์ด๋์ฝ๋๊ฐ ๋์ด๊ฐ๊ฒ ๋๋ค. ์ง๊ธ ์ฝ๋์์ ์ฌ์ฉํ ์ ์๋ ๋ฐฉ๋ฒ์ ๋ค์๋ ๋ฝ์ด๋ค. ๋ค์๋ ๋ฝ์ DB์ ๋ฐ์ดํฐ๊ฐ ์๋ ์๋ , ๋ฉ๋ชจ๋ฆฌ ๊ณต๊ฐ์ ๋ค์ด์จ ์ด๋์ฝ๋๋ฅผ ๊ฐ์์ผ๋ก ๋ง๋ค์ด ๋ฝ์ ๊ฑธ์ด์ ๋์์ฑ์ ๋ง์ ์ ์๋ค.
- ์ธ ๋ฒ์งธ๋ Redis๋ฅผ ์ด์ฉํ ๋ถ์ฐ ๋ฝ์ด๋ค. ์๋ฒ๊ฐ ์ฌ๋ฌ ๋์ธ ํ๊ฒฝ์์ ๊ฐ์ฅ ๋ง์ด ์ฌ์ฉํ๋ ๊ธ๋ก๋ฒ ๋์์ฑ ์ ์ด ๋ฐฉ์์ด๋ค.
- ์ด ์ค ๋ฐ์ดํฐ๋ฒ ์ด์ค Unique ์ ์ฝ ์กฐ๊ฑด์ด ํ์ฌ ์ฝ๋์์ ์ ๋ง๋ค๊ณ ์๊ฐ์ด ๋ค์๋ค. ์ด์ฐจํผ ๊ฐ์ธ ํ๋ก์ ํธ๊ณ ์ค์ ๋ก ์๋ฒ๋ฅผ ์ด์ํ ์๊ฐ์ ์๊ธฐ์ ์ด๋ ๊ฒ ํด๊ฒฐํ๋ค.
- ๊ทธ๋์ ๋ ์ด ์ฝ๋๋ฅผ ๊ณ ์น๋ฉด์ ์๋ก์ด ๋ฌธ์ ๋ฅผ ๋ฐ๊ฒฌํ๋ค. ๊ทธ๊ฑด ๋ฐ๋ก ElasticSearch ์ธ๋ฑ์ค ์ ์ฅ๊ณผ DB์ ๋ถ์ผ์น๊ฐ ๋ฐ์ํ ์ ์๋ค๋ ์ ์ด๋ค. ์ด ๋ถ๋ถ์ ๋ค์์ ์ด๋ป๊ฒ ๊ณ ์ณค๋์ง ์ค๋ช ํด ๋ณด๊ฒ ๋ค.