๐ŸŒฑ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ๊ณผ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ: ๊ฐœ๋ฐœ์ž์˜ ํ•„์ˆ˜ ๋ฌด๊ธฐ ๐Ÿ› ๏ธ

์ฝ˜ํ…์ธ  ๋Œ€ํ‘œ ์ด๋ฏธ์ง€ - ๐ŸŒฑ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ๊ณผ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ: ๊ฐœ๋ฐœ์ž์˜ ํ•„์ˆ˜ ๋ฌด๊ธฐ ๐Ÿ› ๏ธ

 

 

์•ˆ๋…•ํ•˜์„ธ์š”, ์—ฌ๋Ÿฌ๋ถ„! ์˜ค๋Š˜์€ ์ •๋ง ํฅ๋ฏธ์ง„์ง„ํ•œ ์ฃผ์ œ๋กœ ์ฐพ์•„์™”์–ด์š”. ๋ฐ”๋กœ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ๊ณผ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ์— ๋Œ€ํ•ด ๊นŠ์ด ํŒŒํ—ค์ณ๋ณผ ๊ฑฐ์˜ˆ์š”. ์ด๊ฑฐ ์™„์ „ ๊ฐœ๋ฐœ์ž๋“ค์˜ ํ•„์ˆ˜ ๋ฌด๊ธฐ๋ผ๊ณ  ํ•  ์ˆ˜ ์žˆ์ฃ ! ๐Ÿ˜Ž

์—ฌ๋Ÿฌ๋ถ„, ํ˜น์‹œ ๊ฐœ๋ฐœํ•˜๋‹ค๊ฐ€ ์ด๋Ÿฐ ๊ฒฝํ—˜ ์žˆ์œผ์‹ ๊ฐ€์š”? ๋กœ์ปฌ์—์„œ๋Š” ์ž˜ ๋Œ์•„๊ฐ€๋˜ ์ฝ”๋“œ๊ฐ€ ์‹ค์„œ๋ฒ„์— ์˜ฌ๋ฆฌ๋ฉด ๊ฐ‘์ž๊ธฐ '๋ป—์–ด๋ฒ„๋ฆฌ๋Š”' ์ƒํ™ฉ? ์•„๋‹ˆ๋ฉด ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ๊ณผ ์šด์˜ ํ™˜๊ฒฝ์—์„œ ์„ค์ •๊ฐ’์ด ๋‹ฌ๋ผ์„œ ๊ณจ์น˜ ์•„ํŒ ๋˜ ์ ? ใ…‹ใ…‹ใ…‹ ์ด๋Ÿฐ ์ƒํ™ฉ๋“ค, ๋„ˆ๋ฌด ์ต์ˆ™ํ•˜์ง€ ์•Š๋‚˜์š”?

๊ทธ๋ž˜์„œ ์˜ค๋Š˜์€ ์ด๋Ÿฐ ๋ฌธ์ œ๋“ค์„ ํ•œ๋ฐฉ์— ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ๋Š” ์Šคํ”„๋ง์˜ ํ”„๋กœํŒŒ์ผ๊ณผ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ ๊ธฐ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”. ์ด ๊ธฐ์ˆ ๋งŒ ์ œ๋Œ€๋กœ ์ตํžˆ๋ฉด, ์—ฌ๋Ÿฌ๋ถ„๋„ ๊ฐœ๋ฐœ์˜ ์‹ (?)์ด ๋  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค! ๐Ÿ‘ผ

๐ŸŽ“ TMI (Too Much Information) ํƒ€์ž„!

์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—์„œ๋Š” ๋‹ค์–‘ํ•œ ๊ฐœ๋ฐœ ๊ด€๋ จ ๊ฐ•์˜๋„ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ์–ด์š”. ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ ๊ด€๋ฆฌ์— ๋Œ€ํ•œ ๊ณ ๊ธ‰ ๊ธฐ์ˆ ์„ ๋ฐฐ์šฐ๊ณ  ์‹ถ๋‹ค๋ฉด, ์žฌ๋Šฅ๋„ท์—์„œ ์ „๋ฌธ๊ฐ€๋“ค์˜ ๊ฐ•์˜๋ฅผ ๋“ค์–ด๋ณด๋Š” ๊ฒƒ๋„ ์ข‹์€ ๋ฐฉ๋ฒ•์ด์—์š”!

์ž, ๊ทธ๋Ÿผ ์ด์ œ ๋ณธ๊ฒฉ์ ์œผ๋กœ ์‹œ์ž‘ํ•ด๋ณผ๊นŒ์š”? ์ค€๋น„๋˜์…จ๋‚˜์š”? ์•ˆ์ „๋ฒจํŠธ ๊ฝ‰ ๋งค์„ธ์š”! ๐Ÿš€ ์šฐ๋ฆฌ์˜ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ๊ณผ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ ์—ฌํ–‰์ด ์‹œ์ž‘๋ฉ๋‹ˆ๋‹ค!

๐Ÿค” ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ์ด ๋ญ๊ธธ๋ž˜?

์—ฌ๋Ÿฌ๋ถ„, 'ํ”„๋กœํŒŒ์ผ'์ด๋ผ๋Š” ๋ง ๋“ค์–ด๋ณด์…จ๋‚˜์š”? ๋„ค, ๋งž์•„์š”. ์ธ์Šคํƒ€๊ทธ๋žจ์ด๋‚˜ ํŽ˜์ด์Šค๋ถ ๊ฐ™์€ SNS์˜ ๊ทธ ํ”„๋กœํŒŒ์ผ ๋ง๊ณ ์š”! ใ…‹ใ…‹ใ…‹ ์šฐ๋ฆฌ๊ฐ€ ์˜ค๋Š˜ ์ด์•ผ๊ธฐํ•  ํ”„๋กœํŒŒ์ผ์€ ์กฐ๊ธˆ ๋‹ค๋ฅธ ๊ฐœ๋…์ด์—์š”.

์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ์€ ๊ฐ„๋‹จํžˆ ๋งํ•ด์„œ, ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ์„ค์ •์„ ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฅด๊ฒŒ ์ ์šฉํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ด์ฃผ๋Š” ๊ธฐ๋Šฅ์ด์—์š”. ์‰ฝ๊ฒŒ ๋งํ•ด์„œ, ์ƒํ™ฉ์— ๋”ฐ๋ผ ์˜ท์„ ๊ฐˆ์•„์ž…๋Š” ๊ฒƒ๊ณผ ๋น„์Šทํ•˜๋‹ค๊ณ  ์ƒ๊ฐํ•˜๋ฉด ๋ผ์š”!

๐Ÿ’ก ์˜ˆ๋ฅผ ๋“ค์–ด๋ณผ๊นŒ์š”?

  • ๋กœ์ปฌ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ: ํŽธํ•œ ํŠธ๋ ˆ์ด๋‹๋ณต ๐Ÿƒโ€โ™‚๏ธ
  • ํ…Œ์ŠคํŠธ ์„œ๋ฒ„: ๊น”๋”ํ•œ ์บ์ฃผ์–ผ ์˜ท ๐Ÿ‘”
  • ์‹ค์ œ ์šด์˜ ์„œ๋ฒ„: ์ •์žฅ ๐Ÿ‘จโ€๐Ÿ’ผ

์ด๋ ‡๊ฒŒ ์ƒํ™ฉ์— ๋งž๋Š” '์˜ท'์„ ์ž…ํžˆ๋Š” ๊ฑฐ์ฃ !

๊ทธ๋Ÿผ ์ด์ œ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ์˜ ์ฃผ์š” ํŠน์ง•๋“ค์„ ์ž์„ธํžˆ ์‚ดํŽด๋ณผ๊นŒ์š”? ๐Ÿง

1. ํ™˜๊ฒฝ๋ณ„ ์„ค์ • ๋ถ„๋ฆฌ

์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ์˜ ๊ฐ€์žฅ ํฐ ์žฅ์ ์€ ๋ฐ”๋กœ ํ™˜๊ฒฝ๋ณ„๋กœ ์„ค์ •์„ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ค๋Š” ๊ฒƒ์ด์—์š”. ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” ๊ฐœ๋ฐœ์šฉ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ, ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ๋Š” ์šด์˜์šฉ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๊ณ ์š”? ํ”„๋กœํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๋ฉด ์•„์ฃผ ์‰ฝ๊ฒŒ ํ•  ์ˆ˜ ์žˆ์–ด์š”!


# application-dev.properties
spring.datasource.url=jdbc:mysql://localhost:3306/devdb

# application-prod.properties
spring.datasource.url=jdbc:mysql://production-server:3306/proddb

์ด๋ ‡๊ฒŒ ์„ค์ • ํŒŒ์ผ์„ ๋ถ„๋ฆฌํ•ด๋†“์œผ๋ฉด, ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” 'dev' ํ”„๋กœํŒŒ์ผ์„, ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ๋Š” 'prod' ํ”„๋กœํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๋ฉด ๋! ์™„์ „ ํŽธํ•˜์ฃ ? ๐Ÿ˜Ž

2. ์œ ์—ฐํ•œ ๋นˆ ๊ด€๋ฆฌ

ํ”„๋กœํŒŒ์ผ์„ ์‚ฌ์šฉํ•˜๋ฉด ํ™˜๊ฒฝ์— ๋”ฐ๋ผ ๋‹ค๋ฅธ ๋นˆ์„ ๋“ฑ๋กํ•  ์ˆ˜๋„ ์žˆ์–ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ๊ฐ„๋‹จํ•œ ๋ฉ”๋ชจ๋ฆฌ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ , ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์‹ค์ œ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด?


@Configuration
public class DatabaseConfig {

    @Bean
    @Profile("dev")
    public DataSource devDataSource() {
        return new EmbeddedDatabaseBuilder()
            .setType(EmbeddedDatabaseType.H2)
            .build();
    }

    @Bean
    @Profile("prod")
    public DataSource prodDataSource() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
        dataSource.setUrl("jdbc:mysql://production-server:3306/proddb");
        dataSource.setUsername("username");
        dataSource.setPassword("password");
        return dataSource;
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” H2 ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค๋ฅผ, ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ๋Š” MySQL์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”. ์™„์ „ ๊น”๋”ํ•˜์ฃ ? ๐Ÿ‘Œ

3. ํ”„๋กœํŒŒ์ผ ํ™œ์„ฑํ™”

๊ทธ๋Ÿผ ์ด๋ ‡๊ฒŒ ๋งŒ๋“  ํ”„๋กœํŒŒ์ผ์€ ์–ด๋–ป๊ฒŒ ์‚ฌ์šฉํ• ๊นŒ์š”? ๋ฐฉ๋ฒ•์€ ์—ฌ๋Ÿฌ ๊ฐ€์ง€๊ฐ€ ์žˆ์–ด์š”!

  • application.properties ํŒŒ์ผ์—์„œ ์„ค์ •:
    spring.profiles.active=dev
  • JVM ์‹œ์Šคํ…œ ํ”„๋กœํผํ‹ฐ๋กœ ์„ค์ •:
    java -Dspring.profiles.active=prod -jar myapp.jar
  • ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋กœ ์„ค์ •:
    export SPRING_PROFILES_ACTIVE=prod

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์›ํ•˜๋Š” ํ”„๋กœํŒŒ์ผ์„ ์‰ฝ๊ฒŒ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์–ด์š”. ์™„์ „ ํŽธ๋ฆฌํ•˜์ฃ ? ๐Ÿ˜„

๐ŸŒŸ ๊ฟ€ํŒ!

์—ฌ๋Ÿฌ ํ”„๋กœํŒŒ์ผ์„ ๋™์‹œ์— ํ™œ์„ฑํ™”ํ•  ์ˆ˜๋„ ์žˆ์–ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, spring.profiles.active=dev,logging์ฒ˜๋Ÿผ ์„ค์ •ํ•˜๋ฉด 'dev' ํ”„๋กœํŒŒ์ผ๊ณผ 'logging' ํ”„๋กœํŒŒ์ผ์„ ๋™์‹œ์— ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค!

์ž, ์—ฌ๊ธฐ๊นŒ์ง€๊ฐ€ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ์˜ ๊ธฐ๋ณธ์ ์ธ ๊ฐœ๋…์ด์—์š”. ์–ด๋•Œ์š”? ์ƒ๊ฐ๋ณด๋‹ค ์–ด๋ ต์ง€ ์•Š์ฃ ? ์ด์ œ ์ด ๊ฐœ๋…์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด, ์—ฌ๋Ÿฌ๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋งˆ์น˜ ๋ณ€์‹  ๋กœ๋ด‡์ฒ˜๋Ÿผ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์ฒ˜ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”! ๐Ÿค–โœจ

๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์ด ํ”„๋กœํŒŒ์ผ์„ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ํ™˜๊ฒฝ ์„ค์ •์„ ์–ด๋–ป๊ฒŒ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š”์ง€์— ๋Œ€ํ•ด ๋” ์ž์„ธํžˆ ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”. ์ค€๋น„๋˜์…จ๋‚˜์š”? Let's go! ๐Ÿš€

๐Ÿ› ๏ธ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ ์‹ค์ „ ํ™œ์šฉ๋ฒ•

์ž, ์ด์ œ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ์ด ๋ญ”์ง€ ์•Œ์•˜์œผ๋‹ˆ๊นŒ ์‹ค์ œ๋กœ ์–ด๋–ป๊ฒŒ ์“ฐ๋Š”์ง€ ์•Œ์•„๋ณผ๊นŒ์š”? ์—ฌ๋Ÿฌ๋ถ„, ์†์— ๋•€์„ ์ฅ๊ณ  ์ค€๋น„ํ•˜์„ธ์š”! ์ง€๊ธˆ๋ถ€ํ„ฐ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ์˜ ์‹ค์ „ ํ™œ์šฉ๋ฒ•์„ ๋‚ฑ๋‚ฑ์ด ํŒŒํ—ค์ณ๋ณผ ๊ฑฐ์˜ˆ์š”. ๐Ÿ˜Ž

1. ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ • ๋ถ„๋ฆฌํ•˜๊ธฐ

๊ฐ€์žฅ ํ”ํ•œ ์‚ฌ์šฉ ์‚ฌ๋ก€ ์ค‘ ํ•˜๋‚˜๊ฐ€ ๋ฐ”๋กœ ๋ฐ์ดํ„ฐ๋ฒ ์ด์Šค ์„ค์ •์„ ๋ถ„๋ฆฌํ•˜๋Š” ๊ฑฐ์˜ˆ์š”. ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” ๋กœ์ปฌ DB๋ฅผ, ํ…Œ์ŠคํŠธํ•  ๋•Œ๋Š” ํ…Œ์ŠคํŠธ์šฉ DB๋ฅผ, ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ๋Š” ์šด์˜ DB๋ฅผ ์‚ฌ์šฉํ•˜๊ณ  ์‹ถ๋‹ค๋ฉด?


# application-dev.properties
spring.datasource.url=jdbc:h2:mem:devdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=

# application-test.properties
spring.datasource.url=jdbc:mysql://test-server:3306/testdb
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=testuser
spring.datasource.password=testpass

# application-prod.properties
spring.datasource.url=jdbc:mysql://prod-server:3306/proddb
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=produser
spring.datasource.password=prodpass

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•ด๋†“๊ณ , ํ•„์š”ํ•œ ํ”„๋กœํŒŒ์ผ๋งŒ ํ™œ์„ฑํ™”ํ•˜๋ฉด ๋! ์™„์ „ ๊น”๋”ํ•˜์ฃ ? ๐Ÿ‘Œ

๐Ÿ’ก Pro Tip!

๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ™์€ ๋ฏผ๊ฐํ•œ ์ •๋ณด๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋‚˜ ์™ธ๋ถ€ ์„ค์ • ํŒŒ์ผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„์š”. ๋ณด์•ˆ์€ ์•„๋ฌด๋ฆฌ ์‹ ๊ฒฝ ์จ๋„ ์ง€๋‚˜์น˜์ง€ ์•Š๋‹ต๋‹ˆ๋‹ค! ๐Ÿ”’

2. ๋กœ๊น… ๋ ˆ๋ฒจ ์กฐ์ •ํ•˜๊ธฐ

๊ฐœ๋ฐœํ•  ๋•Œ๋Š” ๋กœ๊ทธ๋ฅผ ์ž์„ธํžˆ ๋ณด๊ณ  ์‹ถ๊ณ , ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ๋Š” ์ค‘์š”ํ•œ ๋กœ๊ทธ๋งŒ ๋ณด๊ณ  ์‹ถ๋‹ค๊ตฌ์š”? ํ”„๋กœํŒŒ์ผ๋กœ ์‰ฝ๊ฒŒ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด์š”!


# application-dev.properties
logging.level.root=DEBUG
logging.level.org.springframework.web=DEBUG
logging.level.org.hibernate=DEBUG

# application-prod.properties
logging.level.root=WARN
logging.level.org.springframework.web=ERROR
logging.level.org.hibernate=ERROR

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” ๋ชจ๋“  ๋กœ๊ทธ๋ฅผ ๋ณผ ์ˆ˜ ์žˆ๊ณ , ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ๋Š” ์ค‘์š”ํ•œ ๋กœ๊ทธ๋งŒ ๋ณผ ์ˆ˜ ์žˆ์–ด์š”. ๋กœ๊ทธ ํญํƒ„์—์„œ ํ•ด๋ฐฉ! ๐ŸŽ‰

3. ์บ์‹œ ์„ค์ • ๋‹ค๋ฅด๊ฒŒ ํ•˜๊ธฐ

๊ฐœ๋ฐœํ•  ๋•Œ๋Š” ์บ์‹œ๊ฐ€ ์—†๋Š” ๊ฒŒ ํŽธํ•˜๊ณ , ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ๋Š” ์บ์‹œ๊ฐ€ ์žˆ์–ด์•ผ ๋น ๋ฅด์ฃ ? ์ด๊ฒƒ๋„ ํ”„๋กœํŒŒ์ผ๋กœ ํ•ด๊ฒฐํ•  ์ˆ˜ ์žˆ์–ด์š”!


@Configuration
@EnableCaching
public class CacheConfig {

    @Bean
    @Profile("dev")
    public CacheManager devCacheManager() {
        return new ConcurrentMapCacheManager(); // ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ
    }

    @Bean
    @Profile("prod")
    public CacheManager prodCacheManager() {
        RedisCacheManager.RedisCacheManagerBuilder builder = RedisCacheManager
            .RedisCacheManagerBuilder
            .fromConnectionFactory(redisConnectionFactory());
        return builder.build();
    }

    // Redis ์—ฐ๊ฒฐ ์„ค์ •...
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐœ๋ฐœํ•  ๋•Œ๋Š” ๊ฐ„๋‹จํ•œ ๋ฉ”๋ชจ๋ฆฌ ์บ์‹œ๋ฅผ, ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ๋Š” Redis ์บ์‹œ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”. ์„ฑ๋Šฅ ์ตœ์ ํ™”์˜ ๋ํŒ์™•! ๐Ÿ†

4. ์™ธ๋ถ€ ์„œ๋น„์Šค ์—ฐ๋™ ์„ค์ •

์™ธ๋ถ€ API๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒฝ์šฐ, ๊ฐœ๋ฐœ ํ™˜๊ฒฝ๊ณผ ์šด์˜ ํ™˜๊ฒฝ์˜ ์—”๋“œํฌ์ธํŠธ๊ฐ€ ๋‹ค๋ฅผ ์ˆ˜ ์žˆ์ฃ . ์ด๊ฒƒ๋„ ํ”„๋กœํŒŒ์ผ๋กœ ๊น”๋”ํ•˜๊ฒŒ ํ•ด๊ฒฐ!


@Configuration
public class ExternalServiceConfig {

    @Bean
    @Profile("dev")
    public ExternalService devExternalService() {
        return new ExternalService("https://dev-api.example.com");
    }

    @Bean
    @Profile("prod")
    public ExternalService prodExternalService() {
        return new ExternalService("https://api.example.com");
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐœ๋ฐœํ•  ๋•Œ์™€ ์‹ค์ œ ์„œ๋น„์Šคํ•  ๋•Œ ๋‹ค๋ฅธ API ์—”๋“œํฌ์ธํŠธ๋ฅผ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”. API ํ˜ธ์ถœ ์‹ค์ˆ˜? ์ด์ œ ์•ˆ๋…•~ ๐Ÿ‘‹

5. ์Šค์ผ€์ค„๋ง ์ž‘์—… ์ œ์–ดํ•˜๊ธฐ

๋ฐฐ์น˜ ์ž‘์—…์ด๋‚˜ ์Šค์ผ€์ค„๋ง๋œ ์ž‘์—…์„ ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ๋„๊ณ  ์‹ถ๋‹ค๊ตฌ์š”? ํ”„๋กœํŒŒ์ผ๊ณผ ์กฐ๊ฑด๋ถ€ ๋นˆ ๋“ฑ๋ก์„ ์‚ฌ์šฉํ•˜๋ฉด ๋ฉ๋‹ˆ๋‹ค!


@Configuration
@EnableScheduling
@Profile("!dev") // dev ํ”„๋กœํŒŒ์ผ์ด ์•„๋‹ ๋•Œ๋งŒ ํ™œ์„ฑํ™”
public class SchedulerConfig {

    @Scheduled(fixedRate = 60000)
    public void scheduledTask() {
        // ๋งค ๋ถ„๋งˆ๋‹ค ์‹คํ–‰๋˜๋Š” ์ž‘์—…
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ์Šค์ผ€์ค„๋ง๋œ ์ž‘์—…์ด ์‹คํ–‰๋˜์ง€ ์•Š์•„์š”. ๊ฐœ๋ฐœํ•  ๋•Œ ๋ถˆํ•„์š”ํ•œ ์ž‘์—…์œผ๋กœ ๋ฆฌ์†Œ์Šค ๋‚ญ๋น„? No more! ๐Ÿšซ

๐ŸŽญ ์žฌ๋Šฅ๋„ท TMI

์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—์„œ๋Š” ์ด๋Ÿฐ ๊ณ ๊ธ‰ ์Šคํ”„๋ง ๊ธฐ์ˆ ์„ ํ™œ์šฉํ•œ ํ”„๋กœ์ ํŠธ ์˜๋ขฐ๋„ ๋งŽ์ด ์˜ฌ๋ผ์˜จ๋‹ค๊ณ  ํ•ด์š”. ์—ฌ๋Ÿฌ๋ถ„์˜ ์‹ค๋ ฅ์„ ๋ฝ๋‚ด๋ณผ ๊ธฐํšŒ์˜ˆ์š”! ๐Ÿ˜‰

์ž, ์—ฌ๊ธฐ๊นŒ์ง€๊ฐ€ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ์˜ ์‹ค์ „ ํ™œ์šฉ๋ฒ•์ด์—์š”. ์–ด๋•Œ์š”? ์ƒ๊ฐ๋ณด๋‹ค ๋งŽ์€ ๊ณณ์—์„œ ์œ ์šฉํ•˜๊ฒŒ ์“ธ ์ˆ˜ ์žˆ์ฃ ? ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋งˆ์น˜ ๋ณ€์‹  ๋กœ๋ด‡์ฒ˜๋Ÿผ ์ƒํ™ฉ์— ๋”ฐ๋ผ ์œ ์—ฐํ•˜๊ฒŒ ๋Œ€์ฒ˜ํ•  ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”! ๐Ÿค–โœจ

ํ•˜์ง€๋งŒ ์ž ๊น, ์•„์ง ๋์ด ์•„๋‹ˆ์—์š”! ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์ด๋Ÿฐ ํ”„๋กœํŒŒ์ผ๋“ค์„ ์–ด๋–ป๊ฒŒ ํšจ๊ณผ์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ๋” ๋‚˜์•„๊ฐ€ ์Šคํ”„๋ง์˜ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ ๊ธฐ๋ฒ•์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”. ์ค€๋น„๋˜์…จ๋‚˜์š”? Here we go! ๐Ÿš€

๐ŸŽ›๏ธ ์Šคํ”„๋ง ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ์˜ ์ •์„

์ž, ์ด์ œ ํ”„๋กœํŒŒ์ผ์„ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€ ์•Œ์•˜์œผ๋‹ˆ, ๋” ํฐ ๊ทธ๋ฆผ์„ ๋ณผ ์ฐจ๋ก€์˜ˆ์š”. ๋ฐ”๋กœ ์Šคํ”„๋ง์˜ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ์— ๋Œ€ํ•ด ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”. ์ด๊ฑด ๋งˆ์น˜ ๋ ˆ๊ณ  ๋ธ”๋ก์„ ์กฐ๋ฆฝํ•˜๋Š” ๊ฒƒ๊ณผ ๊ฐ™์•„์š”. ๊ฐ๊ฐ์˜ ์„ค์ •๋“ค์„ ์–ด๋–ป๊ฒŒ ์กฐํ•ฉํ•˜๊ณ  ๊ด€๋ฆฌํ•˜๋Š๋ƒ์— ๋”ฐ๋ผ ์šฐ๋ฆฌ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๋ชจ์Šต์ด ๋‹ฌ๋ผ์ง€์ฃ ! ๐Ÿ—๏ธ

1. ์„ค์ • ํŒŒ์ผ์˜ ๊ณ„์ธต ๊ตฌ์กฐ

์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ์„ค์ • ํŒŒ์ผ์„ ์ฐพ์„ ๋•Œ ์ •ํ•ด์ง„ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ง€๊ณ  ์žˆ์–ด์š”. ์ด๊ฑธ ์ž˜ ํ™œ์šฉํ•˜๋ฉด ์„ค์ •์„ ๋”์šฑ ์ฒด๊ณ„์ ์œผ๋กœ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ๋‹ต๋‹ˆ๋‹ค!

  1. ๋ช…๋ นํ–‰ ์ธ์ˆ˜
  2. ์ž๋ฐ” ์‹œ์Šคํ…œ ํ”„๋กœํผํ‹ฐ
  3. OS ํ™˜๊ฒฝ ๋ณ€์ˆ˜
  4. application.properties (๋˜๋Š” application.yml)

์ด ์ˆœ์„œ๋Œ€๋กœ ์„ค์ •์„ ์ฐพ์•„์š”. ์ฆ‰, ๋ช…๋ นํ–‰ ์ธ์ˆ˜๊ฐ€ ๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ง€๊ณ , application.properties๊ฐ€ ๊ฐ€์žฅ ๋‚ฎ์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ง€๋Š” ๊ฑฐ์ฃ .

๐Ÿ’ก ๊ฟ€ํŒ!

application.properties ํŒŒ์ผ ์•ˆ์—์„œ๋„ ์šฐ์„ ์ˆœ์œ„๊ฐ€ ์žˆ์–ด์š”!

  1. file:./config/
  2. file:./
  3. classpath:/config/
  4. classpath:/

์ด ์ˆœ์„œ๋Œ€๋กœ ์ฐพ์•„์š”. ์ฆ‰, ํ˜„์žฌ ๋””๋ ‰ํ† ๋ฆฌ์˜ config ํด๋”์— ์žˆ๋Š” ์„ค์ • ํŒŒ์ผ์ด ๊ฐ€์žฅ ๋†’์€ ์šฐ์„ ์ˆœ์œ„๋ฅผ ๊ฐ€์ ธ์š”!

2. @ConfigurationProperties ํ™œ์šฉํ•˜๊ธฐ

์„ค์ •๊ฐ’์ด ๋งŽ์•„์ง€๋ฉด ๊ด€๋ฆฌํ•˜๊ธฐ ํž˜๋“ค์–ด์ง€์ฃ ? ์ด๋Ÿด ๋•Œ @ConfigurationProperties๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์„ค์ •๊ฐ’๋“ค์„ ๊ฐ์ฒด๋กœ ๋ฌถ์–ด์„œ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”!


@Configuration
@ConfigurationProperties(prefix = "myapp.datasource")
public class DatabaseProperties {
    private String url;
    private String username;
    private String password;

    // getters and setters
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด application.properties ํŒŒ์ผ์—์„œ ์•„๋ž˜์™€ ๊ฐ™์ด ์„ค์ •ํ•  ์ˆ˜ ์žˆ์–ด์š”:


myapp.datasource.url=jdbc:mysql://localhost:3306/mydb
myapp.datasource.username=root
myapp.datasource.password=secret

์™„์ „ ๊น”๋”ํ•˜์ฃ ? ์ด์ œ ์„ค์ •๊ฐ’๋“ค์ด ๊ฐ์ฒด๋กœ ๋ฌถ์—ฌ์žˆ์œผ๋‹ˆ ๊ด€๋ฆฌํ•˜๊ธฐ๋„ ํŽธํ•˜๊ณ , ํƒ€์ž… ์•ˆ์ •์„ฑ๋„ ๋ณด์žฅ๋ฐ›์„ ์ˆ˜ ์žˆ์–ด์š”! ๐Ÿ‘

3. ํ”„๋กœํŒŒ์ผ๋ณ„ ์„ค์ • ํŒŒ์ผ ๊ด€๋ฆฌ

ํ”„๋กœํŒŒ์ผ๋ณ„๋กœ ์„ค์ • ํŒŒ์ผ์„ ๋”ฐ๋กœ ๋งŒ๋“ค๋ฉด ๊ด€๋ฆฌํ•˜๊ธฐ ๋” ํŽธํ•ด์ ธ์š”. ํŒŒ์ผ ์ด๋ฆ„์„ application-{profile}.properties ํ˜•์‹์œผ๋กœ ๋งŒ๋“ค๋ฉด ๋ผ์š”!

  • application-dev.properties
  • application-test.properties
  • application-prod.properties

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๊ฐ ํ™˜๊ฒฝ๋ณ„ ์„ค์ •์„ ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”. ์„ค์ • ํŒŒ์ผ์ด ๊น”๋”ํ•ด์ง€๋‹ˆ ๋งˆ์Œ๋„ ๊น”๋”~ ๐Ÿ˜Œ

4. ์™ธ๋ถ€ ์„ค์ • ํŒŒ์ผ ์‚ฌ์šฉํ•˜๊ธฐ

์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์™ธ๋ถ€์— ์„ค์ • ํŒŒ์ผ์„ ๋‘๊ณ  ์‹ถ๋‹ค๊ตฌ์š”? ๊ฑฑ์ • ๋งˆ์„ธ์š”! ์Šคํ”„๋ง ๋ถ€ํŠธ๋Š” ์™ธ๋ถ€ ์„ค์ • ํŒŒ์ผ๋„ ์‰ฝ๊ฒŒ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”.


java -jar myapp.jar --spring.config.location=file:///path/to/config/application.properties

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์ง€์ •ํ•œ ๊ฒฝ๋กœ์˜ ์„ค์ • ํŒŒ์ผ์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”. ์„ค์ • ํŒŒ์ผ ๋ณ€๊ฒฝํ•  ๋•Œ๋งˆ๋‹ค ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ๋‹ค์‹œ ๋นŒ๋“œํ•  ํ•„์š” ์—†์–ด์š”! ๐Ÿ‘

5. ์•”ํ˜ธํ™”๋œ ํ”„๋กœํผํ‹ฐ ์‚ฌ์šฉํ•˜๊ธฐ

๋ฏผ๊ฐํ•œ ์ •๋ณด๋Š” ์•”ํ˜ธํ™”ํ•ด์„œ ๊ด€๋ฆฌํ•˜๊ณ  ์‹ถ์ฃ ? Jasypt ๊ฐ™์€ ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์„ค์ • ๊ฐ’์„ ์•”ํ˜ธํ™”ํ•  ์ˆ˜ ์žˆ์–ด์š”!


@Configuration
@EnableEncryptableProperties
public class EncryptionConfig {
    @Bean
    public StringEncryptor stringEncryptor() {
        PooledPBEStringEncryptor encryptor = new PooledPBEStringEncryptor();
        SimpleStringPBEConfig config = new SimpleStringPBEConfig();
        config.setPassword("secretkey"); // ์‹ค์ œ๋กœ๋Š” ํ™˜๊ฒฝ ๋ณ€์ˆ˜ ๋“ฑ์—์„œ ๊ฐ€์ ธ์™€์•ผ ํ•ด์š”!
        config.setAlgorithm("PBEWithMD5AndDES");
        config.setKeyObtentionIterations("1000");
        config.setPoolSize("1");
        config.setProviderName("SunJCE");
        config.setSaltGeneratorClassName("org.jasypt.salt.RandomSaltGenerator");
        config.setStringOutputType("base64");
        encryptor.setConfig(config);
        return encryptor;
    }
}

์ด๋ ‡๊ฒŒ ์„ค์ •ํ•˜๊ณ  ๋‚˜๋ฉด, ์„ค์ • ํŒŒ์ผ์—์„œ ์•”ํ˜ธํ™”๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”:


myapp.datasource.password=ENC(encrypted_password_here)

์ด์ œ ๋น„๋ฐ€๋ฒˆํ˜ธ๊ฐ€ ๋…ธ์ถœ๋  ๊ฑฑ์ •์€ ๋! ๋ณด์•ˆ๋„ ์ฑ™๊ธฐ๊ณ  ์•ˆ์‹ฌ๋„ ๋˜๊ณ , ์ผ์„์ด์กฐ๋„ค์š”! ๐Ÿ˜Ž

๐ŸŒŸ ์žฌ๋Šฅ๋„ท Tip!

์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—์„œ๋Š” ์ด๋Ÿฐ ๊ณ ๊ธ‰ ์„ค์ • ๊ด€๋ฆฌ ๊ธฐ๋ฒ•์„ ์‚ฌ์šฉํ•œ ํ”„๋กœ์ ํŠธ๋“ค์ด ์ธ๊ธฐ๊ฐ€ ๋งŽ๋‹ค๊ณ  ํ•ด์š”. ์—ฌ๋Ÿฌ๋ถ„์˜ ์‹ค๋ ฅ์„ ๋ฝ๋‚ด๋ณผ ์ข‹์€ ๊ธฐํšŒ๊ฐ€ ๋  ์ˆ˜ ์žˆ๊ฒ ์ฃ ? ๐Ÿ’ช

์ž, ์—ฌ๊ธฐ๊นŒ์ง€๊ฐ€ ์Šคํ”„๋ง์˜ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ์— ๋Œ€ํ•œ ๊นŠ์ด ์žˆ๋Š” ์ด์•ผ๊ธฐ์˜€์–ด์š”. ์–ด๋•Œ์š”? ์ƒ๊ฐ๋ณด๋‹ค ๋งŽ์€ ๊ธฐ๋ฒ•๋“ค์ด ์žˆ์ฃ ? ์ด ๊ธฐ๋ฒ•๋“ค์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด, ์—ฌ๋Ÿฌ๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋งˆ์น˜ ๋งŒ๋Šฅ ์š”๋ฆฌ์‚ฌ์ฒ˜๋Ÿผ ์–ด๋–ค ํ™˜๊ฒฝ์—์„œ๋„ ์™„๋ฒฝํ•œ ์š”๋ฆฌ(๊ธฐ๋Šฅ)๋ฅผ ์„ ๋ณด์ผ ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”! ๐Ÿ‘จโ€๐Ÿณโœจ

ํ•˜์ง€๋งŒ ์ž ๊น, ์•„์ง ๋์ด ์•„๋‹ˆ์—์š”! ๋‹ค์Œ ์„น์…˜์—์„œ๋Š” ์ด๋Ÿฐ ์„ค์ •๋“ค์„ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•˜๋Š”์ง€, ๊ทธ๋ฆฌ๊ณ  ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์€ ๋ฌด์—‡์ธ์ง€ ์•Œ์•„๋ณผ ๊ฑฐ์˜ˆ์š”. ์ค€๋น„๋˜์…จ๋‚˜์š”? Let's dive deeper! ๐ŸŠโ€โ™‚๏ธ

๐Ÿš€ ์‹ค์ „! ํ”„๋กœํŒŒ์ผ๊ณผ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ ์ด์ •๋ฆฌ

์ž, ์ด์ œ ์šฐ๋ฆฌ๋Š” ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ๊ณผ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ์— ๋Œ€ํ•ด ๊ฝค๋‚˜ ๊นŠ์ด ์žˆ๊ฒŒ ์•Œ์•„๋ดค์–ด์š”. ๊ทผ๋ฐ ์ด๊ฑธ ์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์–ด๋–ป๊ฒŒ ํ™œ์šฉํ•ด์•ผ ํ• ๊นŒ์š”? ๊ทธ๋ฆฌ๊ณ  ์ฃผ์˜ํ•ด์•ผ ํ•  ์ ์€ ๋ญ˜๊นŒ์š”? ์ง€๊ธˆ๋ถ€ํ„ฐ ์•Œ์•„๋ด ์š”! ๐Ÿง

1. ์‹ค์ œ ํ”„๋กœ์ ํŠธ ๊ตฌ์กฐ ์˜ˆ์‹œ

์‹ค์ œ ํ”„๋กœ์ ํŠธ์—์„œ๋Š” ์ด๋Ÿฐ ์‹์œผ๋กœ ๊ตฌ์กฐ๋ฅผ ์žก์„ ์ˆ˜ ์žˆ์–ด์š”:


src
โ”œโ”€โ”€ main
โ”‚   โ”œโ”€โ”€ java
โ”‚   โ”‚   โ””โ”€โ”€ com
โ”‚   โ”‚       โ””โ”€โ”€ myapp
โ”‚   โ”‚           โ”œโ”€โ”€ config
โ”‚   โ”‚           โ”‚   โ”œโ”€โ”€ DevConfig.java
โ”‚   โ”‚           โ”‚   โ”œโ”€โ”€ ProdConfig.java
โ”‚   โ”‚           โ”‚   โ””โ”€โ”€ CommonConfig.java
โ”‚   โ”‚           โ””โ”€โ”€ MyApplication.java
โ”‚   โ””โ”€โ”€ resources
โ”‚       โ”œโ”€โ”€ application.properties
โ”‚       โ”œโ”€โ”€ application-dev.properties
โ”‚       โ””โ”€โ”€ application-prod.properties

์ด๋ ‡๊ฒŒ ๊ตฌ์„ฑํ•˜๋ฉด ๊ณตํ†ต ์„ค์ •, ๊ฐœ๋ฐœ ํ™˜๊ฒฝ ์„ค์ •, ์šด์˜ ํ™˜๊ฒฝ ์„ค์ •์„ ๊น”๋”ํ•˜๊ฒŒ ๋ถ„๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ฝ”๋“œ๋„ ๊น”๋”, ๋งˆ์Œ๋„ ๊น”๋”! ๐Ÿ˜Œ

2. ํ”„๋กœํŒŒ์ผ ์ „ํ™˜ ์ž๋™ํ™”ํ•˜๊ธฐ

CI/CD ํŒŒ์ดํ”„๋ผ์ธ์„ ๊ตฌ์ถ•ํ•  ๋•Œ, ํ”„๋กœํŒŒ์ผ ์ „ํ™˜์„ ์ž๋™ํ™”ํ•˜๋ฉด ํŽธํ•ด์š”. ์˜ˆ๋ฅผ ๋“ค์–ด, Jenkins๋ฅผ ์‚ฌ์šฉํ•œ๋‹ค๋ฉด:


stage('Deploy') {
    steps {
        sh 'java -jar -Dspring.profiles.active=prod myapp.jar'
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ๋ฐฐํฌ ๋‹จ๊ณ„์—์„œ ์ž๋™์œผ๋กœ prod ํ”„๋กœํŒŒ์ผ์„ ํ™œ์„ฑํ™”ํ•  ์ˆ˜ ์žˆ์–ด์š”. ํœด๋จผ ์—๋Ÿฌ? ์•ˆ๋…•~ ๐Ÿ‘‹

3. ์„ค์ •๊ฐ’ ์œ ํšจ์„ฑ ๊ฒ€์‚ฌ

์„ค์ •๊ฐ’์ด ์˜ฌ๋ฐ”๋ฅธ์ง€ ํ™•์ธํ•˜๋Š” ๊ฒƒ๋„ ์ค‘์š”ํ•ด์š”. @Validated ์–ด๋…ธํ…Œ์ด์…˜์„ ์‚ฌ์šฉํ•˜๋ฉด ์„ค์ •๊ฐ’์˜ ์œ ํšจ์„ฑ์„ ๊ฒ€์‚ฌํ•  ์ˆ˜ ์žˆ์–ด์š”:


@Configuration
@ConfigurationProperties(prefix = "myapp.datasource")
@Validated
public class DatabaseProperties {
    @NotNull
    private String url;

    @NotBlank
    private String username;

    @NotBlank
    @Size(min = 8)
    private String password;

    // getters and setters
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์„ค์ •๊ฐ’์ด ์˜ฌ๋ฐ”๋ฅด์ง€ ์•Š์„ ๋•Œ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹œ์ž‘ ์‹œ์ ์— ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด์š”. ๋ฌธ์ œ๋ฅผ ๋ฏธ๋ฆฌ ์žก์„ ์ˆ˜ ์žˆ์ฃ ! ๐Ÿ‘

4. ๋™์  ํ”„๋กœํŒŒ์ผ ์ „ํ™˜

๋•Œ๋กœ๋Š” ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์‹คํ–‰ ์ค‘์— ํ”„๋กœํŒŒ์ผ์„ ์ „ํ™˜ํ•ด์•ผ ํ•  ์ˆ˜๋„ ์žˆ์–ด์š”. ์ด๋Ÿด ๋•Œ๋Š” ์ด๋ ‡๊ฒŒ ํ•  ์ˆ˜ ์žˆ์–ด์š”:


@Autowired
private ConfigurableEnvironment env;

public void switchToProductionMode() {
    env.setActiveProfiles("prod");
    // ํ•„์š”ํ•œ ๋นˆ๋“ค์„ ๋‹ค์‹œ ๋กœ๋“œํ•˜๋Š” ๋กœ์ง
}

ํ•˜์ง€๋งŒ ์ฃผ์˜ํ•˜์„ธ์š”! ์ด๋ ‡๊ฒŒ ๋™์ ์œผ๋กœ ํ”„๋กœํŒŒ์ผ์„ ์ „ํ™˜ํ•˜๋ฉด ์˜ˆ์ƒ์น˜ ๋ชปํ•œ ๋ฌธ์ œ๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ์–ด์š”. ๊ผญ ํ•„์š”ํ•œ ๊ฒฝ์šฐ๊ฐ€ ์•„๋‹ˆ๋ผ๋ฉด ํ”ผํ•˜๋Š” ๊ฒŒ ์ข‹์•„์š”. ์•ˆ์ „์ œ์ผ! ๐Ÿ›ก๏ธ

5. ์„ค์ •๊ฐ’ ์•”ํ˜ธํ™” ๊ด€๋ฆฌ

์•ž์„œ Jasypt๋ฅผ ์ด์šฉํ•œ ์•”ํ˜ธํ™” ๋ฐฉ๋ฒ•์„ ๋ดค์ฃ ? ์‹ค์ œ๋กœ ์ด๋ฅผ ์ ์šฉํ•  ๋•Œ๋Š” ์•”ํ˜ธํ™” ํ‚ค๋ฅผ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•ด์•ผ ํ•ด์š”. ํ™˜๊ฒฝ ๋ณ€์ˆ˜๋ฅผ ์‚ฌ์šฉํ•˜๋Š” ๊ฒƒ์ด ์ข‹์•„์š”:


export JASYPT_ENCRYPTOR_PASSWORD=mySecretKey
java -jar myapp.jar

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์•”ํ˜ธํ™” ํ‚ค๊ฐ€ ์†Œ์Šค ์ฝ”๋“œ์— ๋…ธ์ถœ๋˜์ง€ ์•Š์•„ ๋” ์•ˆ์ „ํ•ด์š”. ๋ณด์•ˆ์€ ์•„๋ฌด๋ฆฌ ๊ฐ•์กฐํ•ด๋„ ์ง€๋‚˜์น˜์ง€ ์•Š์•„์š”! ๐Ÿ”’

โš ๏ธ ์ฃผ์˜์‚ฌํ•ญ!

  • ๋ฏผ๊ฐํ•œ ์ •๋ณด(DB ๋น„๋ฐ€๋ฒˆํ˜ธ, API ํ‚ค ๋“ฑ)๋Š” ์ ˆ๋Œ€ Git ๋“ฑ์˜ ๋ฒ„์ „ ๊ด€๋ฆฌ ์‹œ์Šคํ…œ์— ์˜ฌ๋ฆฌ์ง€ ๋งˆ์„ธ์š”.
  • ํ”„๋กœํŒŒ์ผ๋ณ„ ์„ค์ • ํŒŒ์ผ์— ์ค‘๋ณต๋œ ์„ค์ •์ด ์žˆ๋‹ค๋ฉด, ์‹ค์ˆ˜๋กœ ์ž˜๋ชป๋œ ๊ฐ’์„ ์‚ฌ์šฉํ•  ์ˆ˜ ์žˆ์–ด์š”. ์ฃผ์˜ํ•ด์„œ ๊ด€๋ฆฌํ•˜์„ธ์š”.
  • ๋„ˆ๋ฌด ๋งŽ์€ ํ”„๋กœํŒŒ์ผ์„ ๋งŒ๋“ค๋ฉด ๊ด€๋ฆฌ๊ฐ€ ์–ด๋ ค์›Œ์งˆ ์ˆ˜ ์žˆ์–ด์š”. ํ•„์š”ํ•œ ๋งŒํผ๋งŒ ๋งŒ๋“œ์„ธ์š”.

์ž, ์—ฌ๊ธฐ๊นŒ์ง€๊ฐ€ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ๊ณผ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ์˜ ์‹ค์ „ ํ™œ์šฉ๋ฒ•์ด์—์š”. ์–ด๋•Œ์š”? ์ด์ œ ์—ฌ๋Ÿฌ๋ถ„์€ ์Šคํ”„๋ง ์„ค์ •์˜ ๋‹ฌ์ธ์ด ๋œ ๊ฒƒ ๊ฐ™์ง€ ์•Š๋‚˜์š”? ๐Ÿ†

์ด ๊ธฐ์ˆ ๋“ค์„ ์ž˜ ํ™œ์šฉํ•˜๋ฉด, ์—ฌ๋Ÿฌ๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์€ ๋งˆ์น˜ ๋ณ€์‹  ๋กœ๋ด‡์ฒ˜๋Ÿผ ์–ด๋–ค ํ™˜๊ฒฝ์—์„œ๋„ ์™„๋ฒฝํ•˜๊ฒŒ ์ž‘๋™ํ•  ๊ฑฐ์˜ˆ์š”. ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์—์„œ๋Š” ๊ฐœ๋ฐœ์ž ์นœํ™”์ ์œผ๋กœ, ํ…Œ์ŠคํŠธ ํ™˜๊ฒฝ์—์„œ๋Š” ํ…Œ์ŠคํŠธ์— ์ตœ์ ํ™”๋˜๊ฒŒ, ๊ทธ๋ฆฌ๊ณ  ์šด์˜ ํ™˜๊ฒฝ์—์„œ๋Š” ์ตœ๊ณ ์˜ ์„ฑ๋Šฅ์„ ๋ฐœํœ˜ํ•˜๋„๋ก ๋ง์ด์ฃ ! ๐Ÿค–โœจ

๊ธฐ์–ตํ•˜์„ธ์š”. ์ข‹์€ ์„ค์ • ๊ด€๋ฆฌ๋Š” ์ข‹์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ธฐ๋ณธ์ด์—์š”. ์—ฌ๋Ÿฌ๋ถ„์˜ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์–ด๋–ค ํ™˜๊ฒฝ์—์„œ๋„ ๋น›์„ ๋ฐœํ•  ์ˆ˜ ์žˆ๊ธฐ๋ฅผ ๋ฐ”๋ž„๊ฒŒ์š”! ํ™”์ดํŒ…! ๐Ÿ’ช๐Ÿ˜„

๐ŸŒŸ ๋งˆ๋ฌด๋ฆฌ: ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ๊ณผ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ์˜ ๋ฏธ๋ž˜

์ž, ์—ฌ๋Ÿฌ๋ถ„! ๊ธด ์—ฌ์ •์ด์—ˆ์ง€๋งŒ ๋“œ๋””์–ด ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ๊ณผ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ์˜ ์„ธ๊ณ„๋ฅผ ํƒํ—˜ํ•˜๊ณ  ๋Œ์•„์™”์–ด์š”. ์–ด๋– ์…จ๋‚˜์š”? ๋จธ๋ฆฌ๊ฐ€ ์กฐ๊ธˆ ์•„ํ”„๋”๋ผ๋„ ๊ดœ์ฐฎ์•„์š”. ์ด๋Ÿฐ ๋ณต์žกํ•œ ๊ฐœ๋…๋“ค์€ ์‹ค์ œ๋กœ ์ ์šฉํ•ด๋ณด๋ฉด์„œ ์ต์ˆ™ํ•ด์ง€๋Š” ๊ฑฐ๋ž๋‹ˆ๋‹ค. ๐Ÿ’ช

๊ทธ๋Ÿฐ๋ฐ ์ž ๊น, ์šฐ๋ฆฌ๊ฐ€ ๋ฐฐ์šด ์ด ๊ธฐ์ˆ ๋“ค์˜ ๋ฏธ๋ž˜๋Š” ์–ด๋–จ๊นŒ์š”? ๐Ÿค” ์Šคํ”„๋ง ํ”„๋ ˆ์ž„์›Œํฌ์™€ ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ์ด ๊ณ„์† ๋ฐœ์ „ํ•˜๋ฉด์„œ, ์„ค์ • ๊ด€๋ฆฌ ๊ธฐ๋ฒ•๋„ ๊ณ„์† ์ง„ํ™”ํ•˜๊ณ  ์žˆ์–ด์š”.

1. ํด๋ผ์šฐ๋“œ ๋„ค์ดํ‹ฐ๋ธŒ ์„ค์ • ๊ด€๋ฆฌ

ํด๋ผ์šฐ๋“œ ํ™˜๊ฒฝ์—์„œ๋Š” ์„ค์ •์„ ๋™์ ์œผ๋กœ ๊ด€๋ฆฌํ•˜๋Š” ๊ฒƒ์ด ๋”์šฑ ์ค‘์š”ํ•ด์ง€๊ณ  ์žˆ์–ด์š”. Spring Cloud Config ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ์ค‘์•™ ์ง‘์ค‘์‹์œผ๋กœ ์„ค์ •์„ ๊ด€๋ฆฌํ•˜๊ณ , ์‹ค์‹œ๊ฐ„์œผ๋กœ ์„ค์ •์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์ฃ .


@RefreshScope
@RestController
public class MessageController {

    @Value("${message:Hello default}")
    private String message;

    @RequestMapping("/message")
    String getMessage() {
        return this.message;
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์„ค์ • ์„œ๋ฒ„์—์„œ ์„ค์ •์„ ๋ณ€๊ฒฝํ•˜๊ณ  '/actuator/refresh' ์—”๋“œํฌ์ธํŠธ๋ฅผ ํ˜ธ์ถœํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์„ ์žฌ์‹œ์ž‘ํ•˜์ง€ ์•Š๊ณ ๋„ ์„ค์ •์„ ๋ณ€๊ฒฝํ•  ์ˆ˜ ์žˆ์–ด์š”. ์™„์ „ ์ฟจํ•˜์ฃ ? ๐Ÿ˜Ž

2. ์ปจํ…Œ์ด๋„ˆํ™”๋œ ํ™˜๊ฒฝ์—์„œ์˜ ์„ค์ • ๊ด€๋ฆฌ

Docker๋‚˜ Kubernetes ๊ฐ™์€ ์ปจํ…Œ์ด๋„ˆ ํ™˜๊ฒฝ์—์„œ๋Š” ์„ค์ •์„ ์–ด๋–ป๊ฒŒ ๊ด€๋ฆฌํ• ๊นŒ์š”? ConfigMap์ด๋‚˜ Secret์„ ์‚ฌ์šฉํ•˜๋ฉด ์ปจํ…Œ์ด๋„ˆ ์™ธ๋ถ€์—์„œ ์„ค์ •์„ ์ฃผ์ž…ํ•  ์ˆ˜ ์žˆ์–ด์š”.


apiVersion: v1
kind: ConfigMap
metadata:
  name: myapp-config
data:
  application.properties: |
    spring.datasource.url=jdbc:mysql://mysql-service:3306/mydb
    spring.datasource.username=root

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜ ์ฝ”๋“œ๋ฅผ ๋ณ€๊ฒฝํ•˜์ง€ ์•Š๊ณ ๋„ ๋‹ค์–‘ํ•œ ํ™˜๊ฒฝ์— ์‰ฝ๊ฒŒ ๋ฐฐํฌํ•  ์ˆ˜ ์žˆ์–ด์š”. ๊ฐœ๋ฐœ์ž์˜ ๊ฟˆ์ด์ฃ ! ๐Ÿš€

3. ๋ณด์•ˆ ๊ฐ•ํ™”

์•ž์œผ๋กœ๋Š” ์„ค์ • ๊ฐ’์˜ ๋ณด์•ˆ์ด ๋”์šฑ ์ค‘์š”ํ•ด์งˆ ๊ฑฐ์˜ˆ์š”. AWS Secrets Manager๋‚˜ HashiCorp Vault ๊ฐ™์€ ๋„๊ตฌ๋ฅผ ์‚ฌ์šฉํ•˜๋ฉด ๋ฏผ๊ฐํ•œ ์ •๋ณด๋ฅผ ๋”์šฑ ์•ˆ์ „ํ•˜๊ฒŒ ๊ด€๋ฆฌํ•  ์ˆ˜ ์žˆ์–ด์š”.


@Configuration
public class SecretConfig {

    @Value("${aws.secretsmanager.secretName}")
    private String secretName;

    @Bean
    public DataSource dataSource() {
        // AWS Secrets Manager์—์„œ DB ์ ‘์† ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์™€ DataSource ์ƒ์„ฑ
    }
}

์ด๋ ‡๊ฒŒ ํ•˜๋ฉด ์†Œ์Šค ์ฝ”๋“œ๋‚˜ ์„ค์ • ํŒŒ์ผ์— ๋ฏผ๊ฐํ•œ ์ •๋ณด๊ฐ€ ๋…ธ์ถœ๋  ์ผ์ด ์—†์–ด์š”. ๋ณด์•ˆ ๋‹ด๋‹น์ž๋„ ์›ƒ๊ณ , ๊ฐœ๋ฐœ์ž๋„ ์›ƒ๊ณ , ๋ชจ๋‘๊ฐ€ ํ–‰๋ณต! ๐Ÿ˜„

๐Ÿ’ก ๋ฏธ๋ž˜๋ฅผ ์œ„ํ•œ Tip!

์„ค์ • ๊ด€๋ฆฌ ๊ธฐ์ˆ ์€ ๊ณ„์† ๋ฐœ์ „ํ•˜๊ณ  ์žˆ์–ด์š”. Spring Boot, Spring Cloud, Kubernetes ๋“ฑ์˜ ์ตœ์‹  ๋™ํ–ฅ์„ ๊ณ„์† ํŒ”๋กœ์šฐํ•˜์„ธ์š”. ์žฌ๋Šฅ๋„ท(https://www.jaenung.net)์—์„œ๋„ ์ด๋Ÿฐ ์ตœ์‹  ๊ธฐ์ˆ ์— ๋Œ€ํ•œ ํ”„๋กœ์ ํŠธ๋‚˜ ๊ฐ•์˜๋ฅผ ์ฐพ์•„๋ณผ ์ˆ˜ ์žˆ์„ ๊ฑฐ์˜ˆ์š”!

์ž, ์ด์ œ ์ •๋ง ๋์ด์—์š”! ์—ฌ๋Ÿฌ๋ถ„์€ ์ด์ œ ์Šคํ”„๋ง ํ”„๋กœํŒŒ์ผ๊ณผ ํ™˜๊ฒฝ ์„ค์ • ๊ด€๋ฆฌ์˜ ํ˜„์žฌ์™€ ๋ฏธ๋ž˜๊นŒ์ง€ ๋ชจ๋‘ ์•Œ๊ฒŒ ๋˜์—ˆ์–ด์š”. ์ด ์ง€์‹์„ ๊ฐ€์ง€๊ณ  ์—ฌ๋Ÿฌ๋ถ„์˜ ํ”„๋กœ์ ํŠธ๋ฅผ ๋”์šฑ ๊ฒฌ๊ณ ํ•˜๊ณ  ์œ ์—ฐํ•˜๊ฒŒ ๋งŒ๋“ค์–ด๋ณด์„ธ์š”.

๊ธฐ์–ตํ•˜์„ธ์š”, ์ข‹์€ ์„ค์ • ๊ด€๋ฆฌ๋Š” ์ข‹์€ ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์˜ ๊ธฐ๋ณธ์ด์—์š”. ์—ฌ๋Ÿฌ๋ถ„์˜ ์ฝ”๋“œ๊ฐ€ ์–ด๋–ค ํ™˜๊ฒฝ์—์„œ๋„ ์™„๋ฒฝํ•˜๊ฒŒ ๋™์ž‘ํ•˜๊ธฐ๋ฅผ ๋ฐ”๋ž„๊ฒŒ์š”. ๊ทธ๋ฆฌ๊ณ  ์–ธ์  ๊ฐ€ ์—ฌ๋Ÿฌ๋ถ„์ด ๋งŒ๋“  ์• ํ”Œ๋ฆฌ์ผ€์ด์…˜์ด ์ „ ์„ธ๊ณ„๋ฅผ ๋†€๋ผ๊ฒŒ ํ•  ๋‚ ์„ ๊ธฐ๋Œ€ํ•˜๊ณ  ์žˆ์„๊ฒŒ์š”! ํ™”์ดํŒ…! ๐ŸŒŸ๐Ÿš€๐Ÿ‘จโ€๐Ÿ’ป