์Šคํ”„๋ง ๋ถ€ํŠธ 3.0 ์œผ๋กœ ์ „ํ™˜

์•ˆ๋…•ํ•˜์„ธ์š”. Dooray ์—์„œ ์ธ์ฆ ์„œ๋ฒ„๋ฅผ ๊ฐœ๋ฐœํ•˜๊ณ  ์žˆ๋Š” ๊น€๋ณ‘๋ถ€์ž…๋‹ˆ๋‹ค.
Dooray ์ธ์ฆ์„œ๋ฒ„๊ฐ€ ์‚ฌ์šฉํ•˜๋Š” Spring Boot ํ”„๋ ˆ์ž„ ์›Œํฌ ๋ฒ„์ „์„ ์—…๊ทธ๋ ˆ์ด๋“œ ํ•˜๋ฉด์„œ ๊ฒช์€ ๋ช‡๊ฐ€์ง€ ๋‚ด์šฉ๋“ค์„ ๊ณต์œ ํ•˜๋ ค๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

2022๋…„ 11์›”์— ๊ธฐ์กด์˜ Spring framework 5 ์™€ Spring Boot 2.X ๋ฒ„์ „์„ ๋Œ€์ฒดํ•˜๋Š” Spring framework 6์™€ Spring Boot 3 ๊ฐ€ ๋ฆด๋ฆฌ์ฆˆ ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.
์ƒˆ๋กœ์šด ์Šคํ”„๋ง ๋ถ€ํŠธ 3.0์€ ์ƒˆ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ๋œ ์ˆ˜๋งŽ์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋“ค์„ ํฌํ•จํ•˜๊ณ  ์žˆ์ง€๋งŒ, ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํฐ ํŠน์ง•์ด ์žˆ์Šต๋‹ˆ๋‹ค.

  • Java17๊ณผ Java19 ์ง€์›
  • GraalVM ์ง€์›
  • Spring framework 6.0 ๊ธฐ๋ฐ˜

์ƒˆ๋กœ์šด ์Šคํ”„๋ง ๋ถ€ํŠธ 3.0 ๋Š” Java17 ์„ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž‘์„ฑ๋œ ์ƒˆ๋กœ์šด ํ”„๋ ˆ์ž„ ์›Œํฌ์ž…๋‹ˆ๋‹ค.

๋งŒ์•ฝ ์—ฌ๋Ÿฌ๋ถ„๋“ค์ด ์•„์ง๋„ Java8 ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, ์Šคํ”„๋ง ๋ถ€ํŠธ 3.0์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.
๊ทธ๋ž˜์„œ ์Šคํ”„๋ง์—์„œ๋„ ์ผ์ • ๊ธฐ๊ฐ„๋™์•ˆ Spring Boot 2.7๊ณผ 3.0์„ ๊ณ„์† ์œ ์ง€ ๋ณด์ˆ˜ํ•  ๊ณ„ํš์ด๋ผ๊ณ  ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ ๊ทธ๋ฆผ์„ ๋ณด๋ฉด ๋ณด์•ˆ ์—…๋ฐ์ดํŠธ๋‚˜ ๋ฒ„๊ทธ ์ˆ˜์ •์€ 2023๋…„ ๋ง๊นŒ์ง€ ์ง€์›ํ•œ๋‹ค๊ณ  ํ•˜๋‹ˆ ๊ฐ€๋Šฅํ•˜๋ฉด ๋น ๋ฅธ ์‹œ๊ฐ„์— ์—…๊ทธ๋ ˆ์ด๋“œ ํ•˜๋Š” ๊ฒƒ์ด ์ข‹๊ฒ ์Šต๋‹ˆ๋‹ค.

์ด๋ฒˆ ์Šคํ”„๋ง ๋ถ€ํŠธ 3.0 ์—๋Š” ์ด๋Ÿฐ ํŠน์ง•๋“ค ์™ธ์—๋„ Jakarta EE ๋ฅผ ์ง€์›ํ•œ๋‹ค๋Š” ํฐ ๋ณ€๊ณก์ ์ด ์ƒ๊ฒผ์Šต๋‹ˆ๋‹ค.
๊ธฐ์กด์— ์šฐ๋ฆฌ๋Š” JavaEE ๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ์Šต๋‹ˆ๋‹ค.

์‚ฌ์šฉ์ž์˜ ์š”์ฒญ์„ ์ฒ˜๋ฆฌํ•˜๊ณ  ์ ์ ˆํ•œ ์‘๋‹ต์„ ์ „๋‹ฌํ•˜๋Š” HttpServletRequest ์™€ HttpServletResponse ๊ฐ€ ๋Œ€ํ‘œ์ ์ธ ํด๋ž˜์Šค์ž…๋‹ˆ๋‹ค. ์ด๋“ค ํด๋ž˜์Šค๋“ค์€ javax.servlet.http ํŒจํ‚ค์ง€์— ํฌํ•จ๋˜์–ด ์žˆ์Šต๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ Spring Boot 3.0 ์—์„œ๋Š” JavaEE ๊ฐ€ ์•„๋‹Œ Jakarta EE ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•˜๋ฏ€๋กœ jakarta.servlet.http.HttpServletRequest, jakarta.servlet.http.HttpServletResponse ๋ฅผ ์†Œ์Šค ์ฝ”๋“œ์— import ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
๊ทธ๋Ÿฌ๋ฏ€๋กœ ๊ธฐ์กด์— Spring Boot 2.X ๋ฅผ 3.0 ์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ ํ•˜๋ ค๋ฉด ์ˆ˜๋งŽ์€ ์ฝ”๋“œ๋ฅผ ์ˆ˜์ •ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

Jakarta EE

2017๋…„ ํ•˜๋ฐ˜๊ธฐ ์˜ค๋ผํด์€ JavaEE ํ”„๋กœ์ ํŠธ๋ฅผ ๋น„์˜๋ฆฌ ๊ธฐ๊ด€์ธ ์ดํด๋ฆฝ์Šค ์žฌ๋‹จ์œผ๋กœ ์ด๊ด€ํ•˜๋Š” ๋‚ด์šฉ์„ ๋ฐœํ‘œํ•ฉ๋‹ˆ๋‹ค.

์ด๋Š” ์‚ฌ์‹ค์ƒ JavaEE ์˜ ๊ธฐ์ˆ  ์ฃผ๋„๊ถŒ์ด ์˜ค๋ผํด์—์„œ ์ดํด๋ฆฝ์Šค ์žฌ๋‹จ์œผ๋กœ ๋„˜์–ด๊ฐ„ ์‚ฌ๊ฑด์ž…๋‹ˆ๋‹ค.
ํ•˜์ง€๋งŒ JavaEE ์ƒํ‘œ๊ถŒ์€ ์˜ค๋ผํด์ด ๊ฐ–๊ณ  ์žˆ์–ด์„œ ์ดํด๋ฆฝ์Šค ์žฌ๋‹จ์—์„œ๋„ JavaEE ํŒจํ‚ค์ง€ ์ด๋ฆ„์ธ javax.* ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค.

์ดํด๋ฆฝ์Šค ์žฌ๋‹จ์€ Jakarta EE ๋ผ๊ณ  ํ•˜๊ณ  ํŒจํ‚ค์ง€๋Š” jakarta.* ๋กœ ๋ช…๋ช…ํ•ฉ๋‹ˆ๋‹ค.
๋‹ค์Œ๊ณผ ๊ฐ™์€ JavaEE ์ด๋ฆ„๋“ค์ด JakartaEE ์ด๋ฆ„์œผ๋กœ ์ƒˆ๋กญ๊ฒŒ ๋ณ€๊ฒฝ๋˜์—ˆ๊ณ , ํŒจํ‚ค์ง€ ์ด๋ฆ„๋กœ ๋ณ€๊ฒฝ๋˜์—ˆ์Šต๋‹ˆ๋‹ค.

  • Java Servlet(javax.servlet) -> Jakarta Servlet(jakarta.servlet)
  • Java Message Servie (javax.jms) -> Jakarta Messaging (jakart.jms)
  • JPA:Java Persistence API (javax.persistence) -> Jakarta Persistence(jakarta.persistence)
  • JTA:Java Transaction API (javax.transaction) -> Jakarta Transaction(jakarta.transaction)
  • Java Mail (javax.mail) -> Jakarta Mail (jakarta.mail)

Spring Boot 2.7.X ์—์„œ 3.0.1 ์œผ๋กœ ์—…๊ทธ๋ ˆ์ด๋“œ

๊ฐ€์žฅ ๋จผ์ € ํ• ์ผ์€ ํ”„๋กœ์ ํŠธ์˜ JDK ๋ฒ„์ „์„ ์˜ฌ๋ฆฌ๋Š” ์ผ์ž…๋‹ˆ๋‹ค. ๊ธฐ์กด์— Java17 ์ด์ƒ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ์—ˆ๋‹ค๋ฉด, ์ˆ˜์ •ํ•  ์ผ์ด ์—†์Šต๋‹ˆ๋‹ค.

๋‘๋ฒˆ์งธ๋Š” pom.xml ์„ ์—ด์–ด์„œ Spring Boot ์˜ ๋ฒ„์ „์„ ์˜ฌ๋ฆฌ๋Š” ์ผ์ž…๋‹ˆ๋‹ค

<parent/>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.0.1</version>
</parent>
    ...
<properties>
    <java.version>17</java.version>
</properties>

์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ ๋ฒ„์ „์„ ์˜ฌ๋ฆฌ๋ฉด JavaEE ๊ฐ€ Jakarta EE ๋กœ ๋ณ€๊ฒฝ๋˜์–ด ํŒจํ‚ค์ง€์™€ ํด๋ž˜์Šค ์‚ฌ์šฉ๋ฒ•์— ๋Œ€ ๋ณ€ํ™”๊ฐ€ ๋ฐœ์ƒํ•ฉ๋‹ˆ๋‹ค.

์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์˜ ๋ฒ„์ „์ด 6.0 ์œผ๋กœ ์˜ฌ๋ผ๊ฐ€๋ฉด์„œ, ํ”„๋ ˆ์ž„ ์›Œํฌ์—์„œ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค์˜ ์‚ฌ์šฉ ๋ฐฉ๋ฒ• ๋˜ํ•œ ๋ณ€๊ฒฝ์ด ์žˆ์Šต๋‹ˆ๋‹ค. ๋งŒ์•ฝ ํ•œ๋ฒˆ์— ํŒจํ‚ค์ง€ ๊ฒฝ๋กœ๋ฅผ ๋ฐ”๊พธ๋ ค๊ณ  IDE ๊ฐ€ ์ œ๊ณตํ•˜๋Š” replace ๊ธฐ๋Šฅ์„ ์‚ฌ์šฉํ•˜๋ฉด ๊ณค๋ž€ํ•ฉ๋‹ˆ๋‹ค.

‘import javax.’ ๋ฌธ์ž์—ด์„ ์ฐพ์•„์„œ ‘import jakarta.’ ๋กœ ๋ณ€๊ฒฝํ•˜์ง€ ๋งˆ์„ธ์š”.
“javax.sql.DataSource”๋‚˜ “javax.crypto.SecretKey” ๊ฐ™์€ ํด๋ž˜์Šค๋Š” JDK ์— ํฌํ•จ๋˜์–ด์žˆ๋Š” ๊ฒƒ๋“ค์ด๋ฉฐ ์ด๋“ค์€ ๋ฐ”๋€Œ์ง€ ์•Š์•˜์œผ๋‹ˆ๊นŒ์š”.
๋Œ€ํ‘œ์ ์ธ ๋ช‡๊ฐ€์ง€ ํด๋ž˜์Šค๋“ค์„ ๋‚˜์—ดํ•˜๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.

import javax.annotation.PostConstruct; 
-> import jakarta.annotation.PostConstruct;

import javax.persistence.EntityManager; 
-> import jakarta.persistence.EntityManager;
import javax.persistence.Column; 
-> import jakarta.persistence.Column;
import javax.persistence.Convert; 
-> import jakarta.persistence.Convert;
import javax.persistence.Entity; 
-> import jakarta.persistence.Entity;

import javax.servlet.http.HttpServletRequest; 
-> import jakarta.servlet.http.HttpServletRequest;

์•„๋งˆ๋„ ์ˆ˜๋งŽ์€ ํด๋ž˜์Šค๋“ค์„ ๋‹ค์‹œ import ํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋งŒ์•ฝ ๋‹ค์Œ๊ณผ ๊ฐ™์€ ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋‹ค๋ฉด, ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ 6.0 ์—์„œ ์ œ๊ณตํ•˜๋Š” ํด๋ž˜์Šค๋“ค๋กœ ๋ณ€๊ฒฝํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
๋‹ค์Œ ํด๋ž˜์Šค๋“ค์€ ๋‹จ์ˆœํžˆ ํŒจํ‚ค์ง€ ์ด๋ฆ„์„ spring5 -> spring6 ๋กœ ๋ณ€๊ฒฝํ•˜๋ฉด ๊ธฐ๋Šฅ์„ ๊ทธ๋Œ€๋กœ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

import org.thymeleaf.spring5.SpringTemplateEngine;
import org.thymeleaf.spring5.templateresolver.SpringResourceTemplateResolver;
import org.thymeleaf.spring5.view.ThymeleafViewResolver; 

์™ธ๋ถ€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์— ์˜์กดํ•˜๋Š” ๊ฒฝ์šฐ์—๋Š” JakartaEE ์— ๋งž๊ฒŒ ๋ณ€๊ฒฝ๋œ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค.
XML ์„ ์ฒ˜๋ฆฌํ•˜๋Š” Jaxb ๋‚˜ JavaMail ์ด ๋Œ€ํ‘œ์ ์ž…๋‹ˆ๋‹ค.

๋‹ค์Œ์€ pom.xml ์˜ˆ์ œ์ด๋ฉฐ, ๋ณ€๊ฒฝ ์ „/ํ›„์„ ๋น„๊ตํ–ˆ์Šต๋‹ˆ๋‹ค.

// Before Spring Boot 3.0
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.1</version>
</dependency>

<dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.7</version>
</dependency>
// After Spring Boot 3.0
<dependency>
    <groupId>jakarta.xml.bind</groupId>
    <artifactId>jakarta.xml.bind-api</artifactId>
    <version>4.0.0</version>
</dependency> 

<dependency>
    <groupId>jakarta.mail</groupId>
    <artifactId>jakarta.mail-api</artifactId>
    <version>2.1.0</version>
</dependency>

๋งŒ์•ฝ ํ”„๋กœ์ ํŠธ๊ฐ€ ์‚ฌ๋‚ด์—์„œ ๋งŒ๋“  ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ(In-House Library)์— ์˜์กดํ•˜๋Š”์ง€ ํ™•์ธํ•  ํ•„์š”๊ฐ€ ์žˆ์Šต๋‹ˆ๋‹ค.

๋งŒ์•ฝ ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๊ฐ€ Spring Framework 6.0 ์—์„œ ์ง€์›ํ•˜์ง€ ์•Š๋Š” ํด๋ž˜์Šค ํ˜น์€ JavaEE ํด๋ž˜์Šค๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ์ ์ ˆํ•œ ํด๋ž˜์Šค๋ฅผ ์ฐพ์„ ์ˆ˜ ์—†์–ด์„œ ์‹คํ–‰๋˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

Query DSL ์„ค์ •

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด Query DSL ์„ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ๋” ๋งŽ์€ ์ฃผ์˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค.

์ด ๊ธ€์„ ์“ฐ๋Š” ์‹œ์ ์—์„œ(2023/01/07) QueryDSL ์ตœ์‹  ๋ฒ„์ „์€ 5.0.0 ์ž…๋‹ˆ๋‹ค.
๊ธฐ์กด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด QueryDSL 5.0.0 ์„ ์‚ฌ์šฉํ•˜๊ณ  ์žˆ๋”๋ผ๋„, JakartaEE ๋ฅผ ์ง€์›ํ•˜๋„๋ก ์„ค์ •์„ ๋ณ€๊ฒฝํ•ด์ค˜์•ผ ํ•ฉ๋‹ˆ๋‹ค.

๋‹ค์Œ์€ pom.xml ์„ ํ™•์ธํ•ด๋ด…์‹œ๋‹ค. classifier ์†์„ฑ์„ ์‚ฌ์šฉํ•˜์—ฌ jakarta ๋ฅผ ์„ค์ •ํ•œ๊ฒƒ์„ ํ™•์ธํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

// FROM
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>5.0.0</version>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>5.0.0</version>
</dependency>
-------------------------------------------------------------
// TO
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-apt</artifactId>
    <version>5.0.0</version>
    <classifier>jakarta</classifier>
    <scope>provided</scope>
</dependency>
<dependency>
    <groupId>com.querydsl</groupId>
    <artifactId>querydsl-jpa</artifactId>
    <version>5.0.0</version>
    <classifier>jakarta</classifier>
</dependency>

QueryDSL ์€ apt-maven-plugin ์„ ์‚ฌ์šฉํ•˜์—ฌ ์ปดํŒŒ์ผ ๊ณผ์ •์—์„œ Q-Type ํด๋ž˜์Šค๋ฅผ ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

ํ•˜์ง€๋งŒ querydsl-apt jakarta ๋ฒ„์ „์—์„œ๋Š” apt-maven-plugin ์ด ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค.
๋งŒ์•ฝ pom.xml ์— apt-maven-plugin ์„ค์ •์ด ์žˆ๋‹ค๋ฉด ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

[INFO] --- maven-compiler-plugin:3.10.1:compile (default-compile) @ 

[INFO] Changes detected - recompiling the module!
[INFO] Compiling 216 source files to ...
Attempt to recreate a file for type com.dooray.idp.domain.QMember
Attempt to recreate a file for type com.dooray.idp.domain.QTenant
...
[INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] Attempt to recreate a file for type com.dooray.idp.domain.QMember
[ERROR] Attempt to recreate a file for type com.dooray.idp.domain.QTenant
...
[INFO] 2 errors

์ด๋•Œ ์—ฌ๋Ÿฌ๋ถ„์ด ์„ค์ •ํ•œ apt-maven-plugin ์„ ์ œ๊ฑฐํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค.
querydsl-apt jakarta ๋ฒ„์ „์—์„œ๋Š” javax.annotation.processing.Processor ๋ฅผ ์„œ๋น„์Šค๋กœ ์ œ๊ณตํ•˜๊ณ  ์žˆ๊ณ , ์ž๋ฐ” ์ปดํŒŒ์ผ๋Ÿฌ๊ฐ€ ์ด๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ Q-class ๋ฅผ target/generated-sources/annotation ์— ์ƒ์„ฑํ•ฉ๋‹ˆ๋‹ค.

๊ทธ๋Ÿฌ๋ฏ€๋กœ ์—ฌ๋Ÿฌ๋ถ„๋“ค์˜ apt-maven-plugin ์ด ์ด์ค‘ ๋™์ž‘ํ•ด์„œ ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•˜๋Š” ๊ฒƒ์ž…๋‹ˆ๋‹ค.

์ด์™€ ๊ด€๋ จ๋œ ์ž์„ธํ•œ ๋‚ด์šฉ์€ ๋‹ค์Œ ๋งํฌ๋กœ ์ฐธ๊ณ ํ•˜์„ธ์š”

์ด ํฌ์ŠคํŠธ๋ฅผ ๊ณต์œ ํ•ด์ฃผ์„ธ์š”
๋ถ€๋ฆ‰
๋ถ€๋ฆ‰

์–ด๋–ป๊ฒŒ ํ•˜๋ฉด ๋” ์˜ค๋žซ๋™์•ˆ ๊ฐœ๋ฐœํ•  ์ˆ˜ ์žˆ์„๊นŒ? ๊ณ ๋ฏผํ•˜๋Š”, ๊ณ ๊ธฐ๋ฅผ ์‚ฌ๋ž‘ํ•˜๋Š” ๊ฐœ๋ฐœ์ž์ž…๋‹ˆ๋‹ค.
โ€˜์Šคํ”„๋ง ๋ถ€ํŠธ๋กœ ๊ฐœ๋ฐœํ•˜๋Š” MSA ์ปดํฌ๋„ŒํŠธโ€™, '์ž๋ฐ”๋ฅผ ๋‹ค๋ฃจ๋Š” ๊ธฐ์ˆ ', '๋ฆฌ๋ˆ…์Šค ๋ฐ”์ด๋ธ”' ๋“ฑ ์—ฌ๋Ÿฌ ์ฑ…์„ ์ผ์–ด์š”.

Articles: 2

์˜๊ฒฌ ๋‚จ๊ธฐ๊ธฐ

0