New Score :0
High Score :0
Run Best
NICE BUSINESS TYPE INDICATOR
3. ๊ธ์ ์ ์น๊ตฌ์๊ฒ ๋น๋ ธ๋๋ฐ ์ค๋์ด ๋์ ์ฃผ๊ธฐ๋ก ํ๋ .. ๊ทธ๋ฐ๋ฐ ์นด๋๊ฐ์ ๋ด์ผํ๋ ๋ ๋ ์ค๋์ธ๋ฐ... ์ด๊ฑธ ์ด์ฉ๋...
4. ์ฐ๋ฆฌ ํ์ฌ๋ ์ค์ํ ์์ฌ ๊ฒฐ์ ์ ํ ๋?
5. ์ด์ฌํ ์ผํ ๋๋ฅผ ์ํ ์ ๋ฌผ์ ์ฃผ๊ณ ์ถ๋ค. ์ด๋ค๊ฒ ์ข์๊น?
6. ์ํ์์ ํฌ์์ํ์ ์ถ์ฒ๋ฐ์๋ค. ์ด๋ค๊ฑธ ๊ฐ์ ํ์ง?
7. ํ์ฌ์์์ ๋๋?
8. ๊ฟ์์ ๊นจ์ด๋๋ 20๋ ์ ์ผ๋ก ๋์๊ฐ๋ค. ๋น์ ์ด ์ ์ผ ๋จผ์ ํ๋์ผ์?
9. ๋ด๊ฐ ์ธ์ฌ ๋ด๋น์๋ผ๋ฉด ์ ๊ท ์ ์ฌ์ ์ฑ์ฉ ์ ์ ์ผ ์ค์ํ๊ฒ ๋ณด๋๊ฒ์?
10. ํ์ฌ์ ์ ๋ง ์ซ์ดํ๋ ๋๋ฃ๊ฐ ์๋ค๋ฉด?
11. ๊ฐ๋ํ ์ง์ ๊ฐ์ฅ์ด ๋์๋ค.. ์๋ ์ ์์ผ ๋ ์ ๋ฌผ์?
12. ํ์ ํ์ฌ ์ถ๊ทผ ์คํ์ผ์?
13.ํ์ฌ ์ฒด์ก๋ํ ํ๋ ๋ ์ด๋ค. ์ค๋ ๋ญํ์ง?
14. ๋์ ์ ๋ฌด ์คํ์ผ์?
Spring Framework 6.2 ๋ฆด๋ฆฌ์ค ๋ ธํธ ๋ฐ ๊ด๋ จ ๊ธฐ์ ๋ํฅ ์ข ํฉ ๋ถ์
Spring Framework 6.2๋ 2024๋ 11์ 14์ผ ์ ์ ์ถ์๋ ์ฃผ์ ๋ฒ์ ์ผ๋ก, ๊ธฐ์กด 6.1 ๋๋น 49๊ฐ ์ด์์ ๊ฐ์ ์ฌํญ๊ณผ ์๋ก์ด ๊ธฐ๋ฅ์ ๋์ ํ์ต๋๋ค. ๋ณธ ๋ณด๊ณ ์๋ ๊ณต์ ๋ฆด๋ฆฌ์ค ๋ ธํธ์ VMware Tanzu์ ์์ฉ ์ ๊ทธ๋ ์ด๋ ๊ฐ์ด๋๋ฅผ ์ค์ฌ์ผ๋ก ๊ธฐ์ ์ ๋ณํ๋ฅผ ์ฌ์ธต ๋ถ์ํฉ๋๋ค.
ํต์ฌ ์ปจํ ์ด๋ ๊ฐ์ ์ฌํญ
1. ์๋ ์ฃผ์ ์๊ณ ๋ฆฌ์ฆ ๊ฐ์
6.2 ๋ฒ์ ์์๋ @Qualifier
์ด๋
ธํ
์ด์
๊ณผ ํ๋ผ๋ฏธํฐ ์ด๋ฆ ๋งค์นญ์ด @Priority
๋ณด๋ค ์ฐ์ ์์๋ฅผ ๊ฐ๋๋ก ๋ณ๊ฒฝ๋์์ต๋๋ค[1][2]. ์ด๋ ํน์ ๋น์ ๋ช
์์ ์ผ๋ก ์ง์ ํ ๋ ๋ ์ง๊ด์ ์ธ ๋์์ ๋ณด์ฅํ๋ฉฐ, ์ฃผ์
๋์ ํ๋ณด ๊ฐ ์ถฉ๋ ๊ฐ๋ฅ์ฑ์ ์ค์
๋๋ค.
@Configuration
public class AppConfig {
@Bean
@Qualifier("primaryService")
MyService primaryService() { ... }
@Bean
@Priority(1)
MyService secondaryService() { ... }
}
์ ๊ตฌ์ฑ์์ @Qualifier
๊ฐ ์ง์ ๋ ๋น์ด ์ฐ์ ์ ํ๋ฉ๋๋ค. ์ด ๋ณ๊ฒฝ์ ํนํ ์ ๋ค๋ฆญ ํ์
์ ์ฌ์ฉํ๋ ๋ณต์กํ ์์กด์ฑ ์ฃผ์
์๋๋ฆฌ์ค์์ ํ์
์์ ์ฑ์ ๊ฐํํฉ๋๋ค.
2. ํด๋ฐฑ ๋น ์ง์
@Fallback
์ด๋
ธํ
์ด์
์ ํตํด ๊ธฐ๋ณธ ๊ตฌํ์ฒด๋ฅผ ์ ๊ณตํ๋ฉด์ ์ฌ์ฉ์ ์ ์ ๋น์ด ์กด์ฌํ ๋ ์๋ ์ ํ๋๋ ๋ฉ์ปค๋์ฆ์ด ๋์
๋์์ต๋๋ค. ์ด๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ๊ฐ๋ฐ์๊ฐ ๊ธฐ๋ณธ ๊ตฌํ์ ์ ๊ณตํ๋ฉด์๋ ์ฌ์ฉ์ ์ปค์คํฐ๋ง์ด์ง์ ์ฉ์ดํ๊ฒ ํฉ๋๋ค.
@Configuration
class SecurityConfig {
@Bean
@Fallback
fun defaultAuthProvider() = DefaultAuthProvider()
}
์ด ๊ตฌ์ฑ์์ ์ฌ์ฉ์๊ฐ AuthProvider
ํ์
์ ๋น์ ์ ์ํ์ง ์์ผ๋ฉด defaultAuthProvider
๊ฐ ์ฌ์ฉ๋๋ฉฐ, ์ ์๋ ๊ฒฝ์ฐ ์ฌ์ฉ์ ๋น์ด ์๋ ์ ํ๋ฉ๋๋ค.
์น ์ ํ๋ฆฌ์ผ์ด์ ์งํ
3. URL ํ์ ๋ณด์ ๊ฐํ
์ต๊ทผ ๋ณด์ ์ทจ์ฝ์ ๋์์ ์ํด RFC 3986 ํ์ค๊ณผ Living URL ํ์ค์ ์ค์ํ๋ ๋ ๊ฐ์ง ์๋ก์ด URL ํ์๊ฐ ์ถ๊ฐ๋์์ต๋๋ค[2][6]. ๊ฐ๋ฐ์๋ ์ ํ๋ฆฌ์ผ์ด์ ์๊ตฌ์ฌํญ์ ๋ฐ๋ผ ์๊ฒฉํ ๊ฒ์ฆ ๋๋ ์ ์ฐํ ์ฒ๋ฆฌ๋ฅผ ์ ํํ ์ ์์ต๋๋ค.
// RFC 3986 ์ค์ ์๊ฒฉ ๋ชจ๋
UriComponents strictUri = UriComponentsBuilder.fromUriString(input, ParserType.RFC).build();
// Living URL ์ ์ฐ ๋ชจ๋
UriComponents lenientUri = UriComponentsBuilder.fromUriString(input, ParserType.WHAT_WG).build();
4. ์ปจํ ์ธ ํ์ ๊ธฐ๋ฐ ์์ธ ์ฒ๋ฆฌ
@ExceptionHandler
๊ฐ ํ๋ก๋์ค ํ์
๋ณ๋ก ๋ถ๊ธฐ ์ฒ๋ฆฌ ๊ฐ๋ฅํด์ ธ ํด๋ผ์ด์ธํธ ์ ํ์ ๋ง๋ ์ค๋ฅ ์๋ต ์์ฑ์ด ๊ฐ๋ฅํด์ก์ต๋๋ค[2][10]. ์ด๋ API ํด๋ผ์ด์ธํธ์ ๋ธ๋ผ์ฐ์ ์ ๊ทผ์ ๋์์ ์ง์ํ๋ ๋ง์ดํฌ๋ก์๋น์ค ์ํคํ
์ฒ์ ํนํ ์ ์ฉํฉ๋๋ค.
@ExceptionHandler(produces = [MediaType.APPLICATION_JSON_VALUE])
fun handleApiException(ex: IllegalArgumentException) =
ResponseEntity.badRequest().body(ApiError(ex.message))
@ExceptionHandler(produces = [MediaType.TEXT_HTML_VALUE])
fun handleWebException(ex: IllegalArgumentException, model: Model): String {
model.addAttribute("error", ex.message)
return "errorPage"
}
ํ ์คํธ ํ๋ ์์ํฌ ํ์
5. AssertJ ํตํฉ ๊ฐํ
Hamcrest ๋์ AssertJ๋ฅผ ํ์ฉํ MockMvc ํ ์คํธ ์ง์์ด ๊ฐ์ ๋์์ต๋๋ค. ํ๋ฃจ์ธํธ API๋ฅผ ํตํด ํ ์คํธ ๊ฐ๋ ์ฑ๊ณผ ์ ์ง๋ณด์์ฑ์ด ํฌ๊ฒ ํฅ์๋์์ต๋๋ค.
val mvc = MvcTester.from(webAppContext)
assertThat(mvc.perform(get("/api/data")))
.hasStatusOk()
.body().json()
.isEqualToIgnoringWhitespace(expectedJson)
6. ๋์ ์์ฑ ๋ฑ๋ก ๊ฐ์
ํ
์คํธ ์ปจํ
์คํธ ๋ด์์ DynamicPropertyRegistrar
๋น์ ํตํด ๋์ ์์ฑ์ ๋ฑ๋กํ ์ ์๊ฒ ๋์์ต๋๋ค. ์ด๋ ํ
์คํธ ํ๊ฒฝ์์ ์ธ๋ถ ์๋น์ค(DB, ์บ์ ๋ฑ)์ ๋์ ๊ตฌ์ฑ ๊ด๋ฆฌ๋ฅผ ๊ฐ์ํํฉ๋๋ค.
@Bean
public DynamicPropertyRegistrar dbProperties() {
return registry -> {
registry.add("db.url", () -> testContainer.getJdbcUrl());
registry.add("db.user", testContainer::getUsername);
};
}
๋ค์ดํฐ๋ธ ์ปดํ์ผ ์ง์ ๊ฐํ
7. ๋ฆฌํ๋ ์ ํํธ ์๋ํ
@RegisterReflection
๊ณผ @ReflectionScan
์ด๋
ธํ
์ด์
์ ํตํด ๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง ์์ฑ ์ ํ์ํ ๋ฆฌํ๋ ์
์ค์ ์ ์๋ํํ์ต๋๋ค[2][10]. ์ด๋ ํนํ ์๋ํํฐ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ ์ ๋ฐ์ํ๋ ๋ค์ดํฐ๋ธ ํธํ์ฑ ๋ฌธ์ ๋ฅผ ํด๊ฒฐํฉ๋๋ค.
@RegisterReflection(targets = [ThirdPartyDTO::class])
@Configuration
class NativeConfig
ํธํ์ฑ ๋ฐ ์ ๊ทธ๋ ์ด๋ ์ ๋ต
8. ์ฃผ์ API ๋ณ๊ฒฝ ์ฌํญ
- Base64Utils ํ๊ธฐ:
java.util.Base64
๋ก ์ ํ ํ์ - WebJars ๋ฆฌ์์ค ๋ฆฌ์กธ๋ฒ:
webjars-locator-lite
๋ก ๋ง์ด๊ทธ๋ ์ด์ ๊ถ์ฅ - HTTP ํค๋ ์ฒ๋ฆฌ:
HttpHeaders
์์ฑ ๋ฐฉ์ ๋ณ๊ฒฝ
// ๋ณ๊ฒฝ ์
HttpHeaders headers = HttpHeaders.writableHttpHeaders(existing);
// ๋ณ๊ฒฝ ํ
HttpHeaders headers = new HttpHeaders(existing);
9. ์ ๊ทธ๋ ์ด๋ ์ฒดํฌ๋ฆฌ์คํธ
- ์์กด์ฑ ๋ฒ์ ๊ฒ์ฆ: Protobuf 4.x, Jackson 2.18+ ํ์
- ๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง ๋น๋: Hibernate 6.5 ์ด์ ์ฌ์ฉ
- ํ ์คํธ ์ฝ๋ ์ ๊ฒ: HtmlUnit 4.2๋ก ๋ง์ด๊ทธ๋ ์ด์
- ๋ณด์ ์ค์ ๊ฒํ : ์๋ก์ด URL ํ์ ์ ์ฉ ์ฌ๋ถ ๊ฒฐ์
์ฑ๋ฅ ์ต์ ํ ์ฌํญ
10. ๋ฐฑ๊ทธ๋ผ์ด๋ ๋น ์ด๊ธฐํ
๊ณ ๋น์ฉ ๋น ์ด๊ธฐํ๋ฅผ ๋น๋๊ธฐ๋ก ์ฒ๋ฆฌํ๋ bootstrap = BACKGROUND
์ต์
์ด ๋์
๋์์ต๋๋ค. ์ด๋ ์ ํ๋ฆฌ์ผ์ด์
์์ ์๊ฐ์ ๋จ์ถํ๋ ๋ฐ ๊ธฐ์ฌํฉ๋๋ค.
@Bean(bootstrap = BootstrapMode.BACKGROUND)
fun heavyResourceLoader() = HeavyResourceLoader()
11. ์ค์ผ์ค๋ง ์ฑ๋ฅ ๊ฐ์
TaskDecorator
๊ฐ ์ค์ผ์ค๋ง ์์
์๋ ์ ์ฉ๋์ด ์ค๋ ๋ ์ปจํ
์คํธ ์ ํ๊ฐ ๊ฐ์ ๋์์ต๋๋ค. ์ด๋ ๋ถ์ฐ ํธ๋ ์ด์ฑ ํ๊ฒฝ์์ ํนํ ์ ์ฉํฉ๋๋ค.
@Bean
TaskDecorator mdcTaskDecorator() {
return runnable -> {
Map context = MDC.getCopyOfContextMap();
return () -> {
MDC.setContextMap(context);
runnable.run();
};
};
}
๋ฏธ๋ ์งํฅ์ ์ํคํ ์ฒ ์ง์
12. ๋ชจ๋ ์น ๊ธฐ์ ํตํฉ
htmx ๋ฐ Hotwired Turbo์ ๊ฐ์ ์ต์ ์น ํ๋ ์์ํฌ๋ฅผ ์ง์ํ๊ธฐ ์ํด ๋ค์ค ๋ทฐ ํ๋๊ทธ๋จผํธ ๋ ๋๋ง์ด ๊ตฌํ๋์์ต๋๋ค. ์ด๋ ์ ์ง์ ํฅ์(Progressive Enhancement) ์ ๋ต ๊ตฌํ์ ์ฉ์ดํ๊ฒ ํฉ๋๋ค.
<!-- htmx ์กฐ๊ฐ ์
๋ฐ์ดํธ ์์ -->
<div hx-get="/partials/header" hx-trigger="load">
<!-- ๋์ ์ฝํ
์ธ ๋ก๋ -->
</div>
13. ์๋ฒ๋ฆฌ์ค ์ค๋น๋ ๊ฐํ
์ง์์๊ฐ ํฌ๋งท(DurationFormat
)์ด ๋จ์ํ๋์ด @Scheduled
์์ "2h30m"๊ณผ ๊ฐ์ ํํ์ด ๊ฐ๋ฅํด์ก์ต๋๋ค. ์ด๋ ๋์ ์ค์ผ์ค๋ง ๊ตฌ์ฑ์ ์ ์ฐ์ฑ์ ๋ํฉ๋๋ค.
@Scheduled(fixedDelayString = "2h30m")
fun runBatchJob() { ... }
๋ณด์ ๊ฐํ ์กฐ์น
14. ์ฟ ํค ๋ถํ ์ ์ฑ (CHIPS)
Third-party ์ฟ ํค ๋ณด์ ๊ฐํ๋ฅผ ์ํด Partitioned ์ฟ ํค ์ง์์ด ์ถ๊ฐ๋์์ต๋๋ค. ์ด๋ ํฌ๋ก์ค ์ฌ์ดํธ ํธ๋ํน ๋ฐฉ์ง ์ ์ฑ ์ ๋์ํ๊ธฐ ์ํ ์กฐ์น์ ๋๋ค.
ResponseCookie cookie = ResponseCookie.from("sessionId", "1234")
.partitioned(true)
.build();
15. ํค๋ ๊ธฐ๋ฐ ๋ฐ์ดํฐ ๋ฐ์ธ๋ฉ
@ModelAttribute
๊ฐ HTTP ํค๋ ๊ฐ์ ์ง์ ๋ฐ์ธ๋ฉํ ์ ์๊ฒ ๋์ด ๋ณด์ ๊ด๋ จ ํค๋ ์ฒ๋ฆฌ ์ฉ์ด์ฑ์ด ๊ฐ์ ๋์์ต๋๋ค.
@PostMapping
fun create(@ModelAttribute dto: RequestDto) { ... }
๊ฒฐ๋ก ๋ฐ ์ ๋ต์ ๊ถ๊ณ
Spring Framework 6.2๋ ํ๋์ ์ ํ๋ฆฌ์ผ์ด์ ๊ฐ๋ฐ์ ์ํ ๋ค์ ์ธ๋ ๊ธฐ์ ์คํ์ ์ ๊ณตํฉ๋๋ค. ์ฃผ์ ์ ๊ทธ๋ ์ด๋ ์ ๋ต์ ์๋ฆฝํ ๋ ๋ค์ ์ฌํญ์ ๊ณ ๋ คํด์ผ ํฉ๋๋ค:
- ์ ์ง์ ๋ง์ด๊ทธ๋ ์ด์ ๊ณํ: Deprecated API ์ฌ์ฉ ํํฉ ๋ถ์ ํ ๋จ๊ณ์ ๊ต์ฒด
- ๋ณด์ ๊ตฌ์ฑ ์ฌ๊ฒ์ฆ: ์๋ก์ด URL ํ์ ๋ฐ ์ฟ ํค ์ ์ฑ ์ ์ฉ ์ฌ๋ถ ๊ฒฐ์
- ๋ชจ๋ํฐ๋ง ์ฒด๊ณ ๊ฐํ: ๊ฐ์ ๋ ๊ด์ธก ๊ฐ๋ฅ์ฑ ๊ธฐ๋ฅ์ ํ์ฉํ ์ค์๊ฐ ์ง๋จ ์์คํ ๊ตฌ์ถ
- ๋ค์ดํฐ๋ธ ์ด๋ฏธ์ง ํ ์คํธ: AOT ์ฒ๋ฆฌ ์ ๋ฆฌํ๋ ์ ํํธ ๋ฑ๋ก ํ๋ก์ธ์ค ๊ฒ์ฆ
- CI/CD ํ์ดํ๋ผ์ธ ๊ฐ์ : AssertJ ๊ธฐ๋ฐ ํ ์คํธ ์ผ์ด์ค๋ก ์ ํ ๋ฐ ๋ณ๋ ฌ ์คํ ์ต์ ํ
ํฅํ Spring Framework 6.3์์๋ Project Loom์ ๊ฐ์ ์ค๋ ๋์์ ํตํฉ ์ฌํ, ๋ ๋์ ์๋ฒ๋ฆฌ์ค ์ง์์ด ์์๋ฉ๋๋ค. ์ง์์ ์ธ ๋ชจ๋ํฐ๋ง์ ํตํด ๊ธฐ์ ๋ก๋๋งต์ ์ ์ ์ ์ผ๋ก ๋์ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.