본문 바로가기
개발 (ENG)

Spring Boot(Kotlin) — ep.10 application.yml Profile Separation (local/dev/prod)

by 새싹 아빠 2025. 11. 27.

In this article, we will configure profiles in a Spring Boot multi-module project and separate environment-specific configurations (local/dev/prod) using application-{profile}.yml.
Profile separation is essential in real-world backend applications because it prevents development and production settings from mixing and helps maintain security and manageability.

 

📌 Spring Boot(Kotlin) Basic Setup — Full Series

  1. Why Multi-Module Architecture? (Architecture philosophy & overall design)
  2. API Response Format Design
  3. Global Exception Handling (GlobalExceptionHandler)
  4. Swagger (OpenAPI) Configuration
  5. Security (JWT) Basic Structure
  6. JWT TokenProvider
  7. Redis Configuration
  8. Validation Setup
  9. Logging + MDC(traceId) Configuration
  10. application.yml Profile Separation (local/dev/prod) ← this article
  11. Multi-Module + JPA Basic Structure
  12. Final Project Template (git) Sharing

 

✔ Why Profile Separation Is Necessary

Backend applications require different configuration values depending on the environment.
For example:

  • Local development → Local DB, local Redis
  • Development server (dev) → Dev DB, dev JWT secret
  • Production server (prod) → Production DB, enhanced security settings

If all configurations are mixed inside a single application.yml, several issues can occur:

  • Production credentials or secrets may leak into source code (security risk)
  • Local testing accidentally connects to dev/prod DB → potential system failure
  • Environment-specific configurations become difficult to manage

Therefore, Spring Boot strongly recommends using profiles to separate environment-specific configurations. In real-world projects, local / dev / prod separation is considered essential.

 

✔ application.yml — Base Configuration

The base application.yml contains only common settings and the default active profile.

application.yml
server:
  port: 8080

spring:
  profiles:
    active: local  # Default is local environment

logging:
  pattern:
    console: "%d{yyyy-MM-dd HH:mm:ss} [traceId=%X{traceId}] [eventId=%X{eventId}] [userId=%X{userId}] [ip=%X{clientIp}] %-5level %logger{36} - %msg%n"

 

Here, we define the console log pattern and set the default active profile to local.


In our previous Logging + MDC setup, we configured multiple MDC fields such as traceId, eventId, userId, and clientIp. After updating the log pattern based on that configuration, the actual log output will look like the following:

2025-02-01 23:10:22 [traceId=ca21ef01] [eventId=ABCD1234] [userId=42] [ip=123.45.67.89] ERROR c.e.a.UserService - [Unexpected Error]
method=com.example.UserService.createUser(L52)
rootCause=java.lang.IllegalStateException: something went wrong

With this format, it becomes significantly easier to trace individual requests, identify the exact user who triggered the error, and quickly investigate issues across different environments.

 

✔ application-dev.yml — Development Environment

application-dev.yml

spring:
  config:
    activate:
      on-profile: dev

  datasource:
    url: ${DEV_DB_URL}
    username: ${DEV_DB_USERNAME}
    password: ${DEV_DB_PASSWORD}
    driver-class-name: com.mysql.cj.jdbc.Driver

  jpa:
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        format_sql: false
    open-in-view: false

jwt:
  secret: ${DEV_JWT_SECRET}

redis:
  host: ${REDIS_HOST}
  port: ${REDIS_PORT}

The development environment is usually deployed on AWS, GCP, or an internal dev server.
All critical values (DB, Redis, JWT secret) are injected via environment variables.

 

✔ application-local.yml — Local Development

application-local.yml

spring:
  config:
    activate:
      on-profile: local

  datasource:
    url: ${LOCAL_DB_URL:}
    username: ${LOCAL_DB_USERNAME:}
    password: ${LOCAL_DB_PASSWORD:}
    driver-class-name: com.mysql.cj.jdbc.Driver

  jpa:
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        format_sql: true
    open-in-view: false

# Base64 encoded dummy key (real secrets should be overridden in dev/prod)
jwt:
  secret: dGVtcG9yYXJ5LXNlY3JldC1rZXktZm9yLWxvY2FsLWRldg==

redis:
  host: localhost
  port: 6379

For local development:

  • format_sql = true → SQL logs become easier to read
  • dummy JWT key → prevents real secrets from being stored in source code
  • Local DB & Redis used for convenience

 

✔ application-prod.yml — Production Environment

application-prod.yml

spring:
  config:
    activate:
      on-profile: prod

  datasource:
    url: ${PROD_DB_URL}
    username: ${PROD_DB_USERNAME}
    password: ${PROD_DB_PASSWORD}
    driver-class-name: com.mysql.cj.jdbc.Driver

  jpa:
    hibernate:
      ddl-auto: none
    properties:
      hibernate:
        format_sql: false
    open-in-view: false

jwt:
  secret: ${PROD_JWT_SECRET}

redis:
  host: ${REDIS_HOST}
  port: ${REDIS_PORT}

In production:

  • Production DB credentials are never stored in source code
  • SQL formatting disabled (format_sql = false) for performance
  • JWT secret loaded fromprod-level environment variables
  • All external service credentials use environment variables

 

✔ How Profile Resolution Works

Spring Boot loads configurations in the following order:

application.yml → check active profile → 
application-{profile}.yml → override settings

To run in production:

java -jar app.jar --spring.profiles.active=prod

→ application.yml is loaded → application-prod.yml overrides environment-specific settings

 

✔ Why Profile Separation Is Beneficial

1) Security

  • Production DB credentials and JWT secrets never leak into source code
  • Everything sensitive is injected via environment variables

2) Prevent Configuration Mistakes

  • Prevents accidental use of production DB during local development
  • Dev environment testing won't affect real production data

3) Deploy Easily with CI/CD

  • Simply change the profile during deployment
  • One codebase supports three environments

4) Easier Maintenance

  • Environment-specific settings are clearly separated
  • Team collaboration becomes safer and smoother

 

✔ Conclusion

Separating application.yml profiles is essential even for small projects.
Once your project has development and production environments, profile separation dramatically improves security, stability, and maintainability.

The next article will cover Multi-Module + JPA Basic Structure.

 

https://jaemoi8.tistory.com/49

 

Spring Boot(Kotlin) — ep.11 Structuring JPA in a Spring Boot Multi-Module Architecture

In this article, we will organize how JPA should be placed within a Spring Boot multi-module projectand summarize the key concepts you need to understand during the initial setup.Since the goal of this series is to build a “basic server setup template,

jaemoi8.tistory.com