
๐ฆ 2ํธ. Jackson ์ง๋ ฌํ ํ๋ — CPU๋ฅผ ๊ฐ์ฅ ๋ง์ด ์ก์๋จน๋ ์จ์ ๋ณ๋ชฉ ํด๊ฒฐ๊ธฐ
Spring Boot ์๋น์ค๋ฅผ ์ด์ํ๋ค ๋ณด๋ฉด, DB๋ ์ธ๋ถ API์ฒ๋ผ ๋ช
ํํ ๋ณ๋ชฉ๋ง ๋ฌธ์ ๊ฐ ์๋๋ค.
APM์ ์์ธํ ๋ณด๋ฉด ๊ฝค ๋ง์ ํธ๋์ญ์
์์ ObjectMapper(JSON ์ง๋ ฌํ) ๊ฐ ์ ์ฒด ์๋ต ์๊ฐ์ 20~40%๋ฅผ ์ฐจ์งํ๋ ๊ฒฝ์ฐ๊ฐ ์๋ค.
์ด๋ฒ ํธ์์๋ ์ด์ ํ๊ฒฝ์์ ์ค์ ๋ก ๊ฒช์๋ “Jackson ์ง๋ ฌํ ๋ณ๋ชฉ” ์ด์๋ฅผ ๊ธฐ์ค์ผ๋ก,
์ด๋ป๊ฒ CPU๋ฅผ ์ ๋ฐ ์ดํ๋ก ์ค์๋์ง ์ค์ ์ค์ฌ์ผ๋ก ์ ๋ฆฌํด๋ณธ๋ค.
โ ๋ฌธ์ 2: Jackson ์ง๋ ฌํ๊ฐ API ์๋ต์ ๋๋ถ๋ถ์ ์ฐจ์ง
API ์๋ต์ด ๋๋ ค์ง๋ ์์ธ์ ์ฐพ์๋ณด๋ฉด ์์ธ๋ก SQL๋ ๋น ๋ฅด๊ณ , ๋น์ฆ๋์ค ๋ก์ง๋ ๋น ๋ฅธ๋ฐ
๋ง์ง๋ง ๋จ๊ณ์ธ JSON ๋ณํ ๋ถ๋ถ์์๋ง ์์ญ ms์ฉ ์ก์๋จน๋ ๊ฒฝ์ฐ๊ฐ ๋ง๋ค.
์ด์ ์๋ฒ์์ ๊ด์ธก๋ ๋ฌธ์ :
- ObjectMapper.writeValueAsString ๋จ๊ณ์์๋ง 50~120ms ์๋ชจ
- ๋๋ ๋ฆฌ์คํธ(1000๊ฑด ์ด์) ์๋ต์์ CPU ์ฌ์ฉ๋ฅ ๊ธ์ฆ
- ๋์ ์์ฒญ ์ Tomcat ์ค๋ ๋๊ฐ ์ง๋ ฌํ ๋๋ฌธ์ ๋งํ๋ฒ๋ฆผ
- GC ๋น๋ ์ฆ๊ฐ → ์ ์ฒด API ์๋ต์ด ์ง์ฐ
ํนํ ๊ธ์ต๊ถ์ฒ๋ผ ์๋ต ๊ฐ์ฒด๊ฐ ๋ณต์กํ๊ณ ํ๋๊ฐ ๋ง์์๋ก ์ง๋ ฌํ ๋น์ฉ์ ๋ ์ปค์ง๋ค.
โ ์์ธ ๋ถ์
1) Lombok ์ @Data + Getter ์กฐํฉ ๋ฌธ์
@Data ๋ equals, hashcode, toString ๊น์ง ์์ฑํด๋ฒ๋ฆฌ๋๋ฐ,
Jackson ์ง๋ ฌํ ์ ๋ฆฌํ๋ ์
๋น์ฉ์ด ์ปค์ง๊ณ ํ๋๋ฅผ ์ค์บํ๋ ๋น์ฉ๋ ์ฆ๊ฐํ๋ค.
2) ๋ถํ์ํ null ํ๋๋ ๋ชจ๋ JSON ์ผ๋ก ์ถ๋ ฅ
ํ๋ ๊ฐ์๊ฐ ๋ง์์ง์๋ก ์ง๋ ฌํ ์๊ฐ์ด ๊ธฐํ๊ธ์์ ์ผ๋ก ์ฆ๊ฐํ๋ค.
3) LocalDateTime ๋ฑ ๋ ์ง ํ์ ๋ณํ ๋น์ฉ์ด ๋์
Java 8 ๋ ์ง ํ์ ์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง๋ ฌํ ๋น์ฉ์ด ๊ฝค ํฌ๋ค.
4) ObjectMapper ์ธ์คํด์ค๋ฅผ ๋งค ์์ฒญ๋ง๋ค ์์ฑ
Controller·Service ๋ด๋ถ์์ ObjectMapper๋ฅผ NEW ํด์ ์ฐ๋ฉด ์ง๋ ฌํ ๋น์ฉ ํญ์ฆ.
โ ํด๊ฒฐ ๋ฐฉ๋ฒ
์ด์ ํ๊ฒฝ์์ ๊ฐ์ฅ ํจ๊ณผ๊ฐ ์ปธ๋ ํจํด์ ์ ๋ฆฌํ๋ฉด ์๋์ ๊ฐ๋ค.
โ 1) ObjectMapper Bean์ ์ ์ญ ์ฑ๊ธํด์ผ๋ก ๋ฑ๋ก
@Configuration
public class JacksonConfig {
@Bean
@Primary
public ObjectMapper objectMapper() {
return JsonMapper.builder()
.enable(SerializationFeature.WRITE_ENUMS_USING_TO_STRING)
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)
.serializationInclusion(JsonInclude.Include.NON_NULL) // null ํ๋ ์ ๊ฑฐ
.build();
}
}
ํจ๊ณผ: ์ง๋ ฌํ ์๊ฐ 20~30% ๊ฐ์
โ 2) DTO์์ @JsonInclude / @JsonIgnore ์ ๊ทน ์ฌ์ฉ
ํ์ ์๋ ํ๋๋ฅผ ๊ณผ๊ฐํ ์ ์ธํ๋ฉด ์ง๋ ฌํ ๋น์ฉ์ ๊ทธ๋๋ก ์ค์ด๋ ๋ค.
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseDto {
private String name;
@JsonIgnore
private String internalCode;
}
ํจ๊ณผ: ํ๋๊ฐ ๋ง์ ์๋ต ๊ฐ์ฒด์์ CPU ์ฌ์ฉ๋ ๊ฐ์
โ 3) ๋ ์ง ์ง๋ ฌํ๋ฅผ ์ํ ๋ชจ๋ ์ ์ฉ
objectMapper.registerModule(new JavaTimeModule());
๋๋ ์ง์ ํฌ๋งท ์ง์ :
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime createdAt;
ํจ๊ณผ: LocalDateTime ์ง๋ ฌํ ๋น์ฉ ์์ ํ
โ 4) @Data ๋์ @Getter + @Setter ์ฌ์ฉ
@Getter
@Setter
public class UserResponse {
private Long id;
private String name;
}
@ToString, @EqualsAndHashCode ์๋์์ฑ ๋น์ฉ์ ์ ๊ฑฐํจ์ผ๋ก์จ
๊ฐ์ฒด ์ค์บ ๋ฐ ์ง๋ ฌํ ๋น์ฉ์ด ์ค์ ๋ก 10~20% ๊ฐ์ํ๋ค.
โ 5) ๋๋ ๋ฆฌ์คํธ๋ “Stream → DTO ๋ณํ → JSON” ๋์ ๋ฐ๋ก Writer ์ฌ์ฉ
objectMapper.writeValue(outputStream, resultList);
ํจ๊ณผ: ์ค๊ฐ ๊ฐ์ฒด ์์ฑ(GC) ๊ฐ์ → TPS ํฅ์
โ ์ ์ฉ ํ ์ฑ๋ฅ ๊ฐ์ ํจ๊ณผ
์ด์ ์๋ฒ ๊ธฐ์ค:
ํญ๋ชฉ ๊ฐ์ ์ ๊ฐ์ ํ
| JSON ์ง๋ ฌํ ํ๊ท | 60~120ms | 18~35ms |
| CPU ์ฌ์ฉ๋ฅ | 65% ์ด์ | 40% ์ดํ |
| GC ๋น๋ | 2~3๋ฐฐ ๊ฐ์ | ์ ๋ฐ ์ดํ |
| TPS(์ด๋น ์์ฒญ ์ฒ๋ฆฌ๋) | +15~20% ์ฆ๊ฐ |
์ด์ฒ๋ผ Jackson ํ๋์ ๋จ์ํ ์ต์ ํ๊ฐ ์๋๋ผ
์ ์ฒด API ์๋ต ์ง์ฐ์ ์ค์ด๋ ํต์ฌ ์์๊ฐ ๋๋ค.
โ ์ค์ ํ 3๊ฐ (์ด์ ํ๊ฒฝ ๊ธฐ์ค)
1. @Data ๋ฅผ ์์ ์ฌ์ฉํ์ง ๋ง๋ผ — ์ง๋ ฌํ ๋น์ฉ ์ฆ๊ฐ์ ์ฃผ๋ฒ
ํ์ํ ๋ฉ์๋๋ง ๋ช ์์ ์ผ๋ก ์์ฑํ๋ ๊ฒ์ด ๋ ํจ์จ์ ์ด๋ค.
2. null ํ๋๋ฅผ ์ ๊ฑฐํ๋ ๊ฒ๋ง์ผ๋ก๋ ์ง๋ ฌํ ์๋๋ ๋์ ๋๊ฒ ๋นจ๋ผ์ง๋ค
ํนํ ๊ธ์ต ๋ฐ์ดํฐ์ฒ๋ผ ํ๋๊ฐ 30๊ฐ ์ด์์ด๋ผ๋ฉด ํจ๊ณผ๊ฐ ํฌ๋ค.
3. ObjectMapper ๋ ๋ฐ๋์ ์ฑ๊ธํด Bean ์ผ๋ก ๊ด๋ฆฌํ๋ผ
๋งค๋ฒ ์์ฑํ๋ฉด CPU·GC ๋ชจ๋ ํญ๋ฐํ๋ค.
๐งฉ ๋ง๋ฌด๋ฆฌ
์ด๋ฒ ํธ์์๋ Spring Boot ์ด์ ํ๊ฒฝ์์ ์ค์ ๋ก ํจ๊ณผ๊ฐ ์ปธ๋ Jackson ์ง๋ ฌํ ์ฑ๋ฅ ํ๋์ ๋ค๋ค๋ค.
๋ค์ ๊ธ์์๋ ThreadPool ํ๋, Tomcat NIO ์ค์ , slow request trace ๋ถ์ ๊ฐ์ ๋ ๊น์ ์ด์ ๊ฒฝํ์ ์ด์ด์ ๋ค๋ฃฐ ์์ ์ด๋ค.
๐ TAGS
Java, SpringBoot, Jackson, JSON, PerformanceTuning, BackendOptimization, CPUOptimization, ProductionIssue, RealWorldTips
- 1ํธ — ์ด์ ์๋ฒ์์ ์ค์ ๋ก ํจ๊ณผ ๋ณธ ๋ฐฉ๋ฒ
- 2ํธ — Jackson ์ง๋ ฌํ ํ๋: ์จ์ CPU ๋ณ๋ชฉ ํด๊ฒฐ (ํ์ฌ ๊ธ)
- 3ํธ — ThreadPool & Tomcat ํ๋: ํธ๋ํฝ ํญ์ฃผ ๋์
- 4ํธ — DB ์ฑ๋ฅ ๋ชจ๋ํฐ๋ง๊ณผ SQL ํ๋ ์ฌ๋ก
- 5ํธ — ์ด์ ์ฅ์ ์ฌ๋ก 10์ + ์ฅ์ ๋ณต๊ตฌ ํ๋ ์ด๋ถ