AI 프롬프트 사용 예시

기술 질문 예시

예시 1: 성능 최적화 질문

User: Spring Boot 애플리케이션의 응답 시간을 개선하고 싶어. 현재 평균 500ms인데 200ms 이하로 줄이려면 어떻게 해야 할까?

AI Assistant:

문제 이해

Spring Boot 애플리케이션의 응답 시간을 500ms에서 200ms 이하로 개선하려는 요구사항입니다.

해결 방안

방안 1: 데이터베이스 쿼리 최적화

  • 장점: 가장 큰 성능 향상 기대
  • 단점: 쿼리 분석 시간 필요
  • 예제 코드:
// N+1 문제 해결 - Fetch Join 사용
@Query("SELECT u FROM User u LEFT JOIN FETCH u.orders WHERE u.id = :userId")
fun findUserWithOrders(@Param("userId") userId: Long): User?
 
// 인덱스 추가
@Table(indexes = [
    Index(name = "idx_user_email", columnList = "email"),
    Index(name = "idx_created_at", columnList = "createdAt")
])

방안 2: 캐싱 전략 구현

  • 장점: 즉각적인 성능 향상
  • 단점: 캐시 일관성 관리 필요
  • 예제 코드:
@Configuration
@EnableCaching
class CacheConfig {
    @Bean
    fun cacheManager(): CacheManager {
        return ConcurrentMapCacheManager("users", "products")
    }
}
 
@Cacheable(value = ["users"], key = "#userId")
suspend fun findUser(userId: Long): User {
    return userRepository.findById(userId)
}

추천 솔루션

데이터베이스 쿼리 최적화와 캐싱을 함께 적용하는 것을 추천합니다.

구현 단계

  • 느린 쿼리 식별 (Spring Boot Actuator 활용)
  • 쿼리 실행 계획 분석
  • 인덱스 추가 및 쿼리 최적화
  • Redis 캐시 도입
  • 성능 측정 및 모니터링

참고 자료

예시 2: 아키텍처 설계 질문

User: 우리 서비스를 모놀리식에서 MSA로 전환하려고 하는데, 어떤 순서로 진행하면 좋을까?

AI Assistant:

문제 이해

모놀리식 아키텍처를 마이크로서비스 아키텍처(MSA)로 단계적으로 전환하는 전략이 필요합니다.

전환 전략

1단계: 도메인 경계 식별

// 도메인 모듈 분리 예시
modules/
├── user-domain/
├── order-domain/
├── payment-domain/
└── notification-domain/

2단계: Strangler Fig 패턴 적용

  • 기존 모놀리스를 유지하면서 점진적 분리
  • API Gateway를 통한 라우팅
// API Gateway 설정
@Configuration
class GatewayConfig {
    @Bean
    fun routeLocator(builder: RouteLocatorBuilder): RouteLocator {
        return builder.routes()
            .route("user-service") { r ->
                r.path("/api/users/**")
                    .uri("http://user-service:8081")
            }
            .route("legacy-monolith") { r ->
                r.path("/**")
                    .uri("http://monolith:8080")
            }
            .build()
    }
}

3단계: 데이터베이스 분리

  • 공유 데이터베이스에서 서비스별 DB로 전환
  • 이벤트 소싱 패턴 도입
  • Saga 패턴으로 분산 트랜잭션 처리

4단계: 서비스 간 통신

// gRPC 통신 예시
@GrpcService
class UserGrpcService : UserServiceGrpcKt.UserServiceCoroutineImplBase() {
    override suspend fun getUser(request: GetUserRequest): UserResponse {
        return userService.findById(request.userId)
            .toGrpcResponse()
    }
}

주의사항

  • 분산 트랜잭션 처리 전략 수립
  • 서비스 간 통신 실패 대응
  • 모니터링 및 추적 시스템 구축
  • 조직 구조 변경 고려

참고 자료

코드 리뷰 예시

예시 3: 코드 개선 요청

User: 이 코드 좀 리뷰해줘:

class UserService {
    fun getUsers(): List<User> {
        val users = mutableListOf<User>()
        val result = jdbcTemplate.query("SELECT * FROM users")
        for (row in result) {
            val user = User()
            user.id = row.getLong("id")
            user.name = row.getString("name")
            user.email = row.getString("email")
            users.add(user)
        }
        return users
    }
}

AI Assistant:

코드 분석

전반적으로 개선이 필요한 레거시 스타일의 코드입니다. Kotlin과 Spring의 모던한 기능을 활용하지 못하고 있습니다.

개선 사항

1. 의존성 주입 및 Repository 패턴 적용

현재 코드: 직접 jdbcTemplate 사용 개선된 코드:

@Repository
class UserRepository(
    private val jdbcTemplate: JdbcTemplate
) {
    fun findAll(): List<User> = jdbcTemplate.query(
        "SELECT id, name, email FROM users",
        UserRowMapper()
    )
}
 
@Service
class UserService(
    private val userRepository: UserRepository
) {
    fun getUsers(): List<User> = userRepository.findAll()
}

2. 데이터 클래스 및 RowMapper 활용

현재 코드: Mutable 객체와 수동 매핑 개선된 코드:

data class User(
    val id: Long,
    val name: String,
    val email: String
)
 
class UserRowMapper : RowMapper<User> {
    override fun mapRow(rs: ResultSet, rowNum: Int) = User(
        id = rs.getLong("id"),
        name = rs.getString("name"),
        email = rs.getString("email")
    )
}

3. Spring Data JPA로 마이그레이션 고려

@Entity
@Table(name = "users")
data class User(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    val id: Long = 0,
    val name: String,
    val email: String
)
 
@Repository
interface UserRepository : JpaRepository<User, Long>

모범 사례 적용

  • 의존성 주입 패턴
  • 불변 데이터 클래스
  • Repository 패턴
  • 함수형 프로그래밍 스타일
  • 페이징 처리 추가
  • 예외 처리 개선

추가 권장사항

  • 대용량 데이터 처리 시 페이징 구현
  • 캐싱 전략 도입 검토
  • 비동기 처리 고려 (Coroutines)

문제 해결 예시

예시 4: 에러 해결 요청

User: Spring Boot 애플리케이션 시작할 때 이런 에러가 나와:

BeanCreationException: Error creating bean with name 'dataSource': 
Failed to determine a suitable driver class

AI Assistant:

문제 이해

데이터베이스 드라이버 클래스를 찾지 못해 DataSource 빈 생성에 실패하는 문제입니다.

해결 방법

1. 의존성 확인

build.gradle.kts:

dependencies {
    // 사용하는 DB에 맞는 드라이버 추가
    implementation("org.postgresql:postgresql") // PostgreSQL
    // implementation("mysql:mysql-connector-java") // MySQL
    // implementation("com.h2database:h2") // H2
}

2. application.yml 설정

spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb
    username: user
    password: password
    driver-class-name: org.postgresql.Driver # 명시적 지정

3. 프로파일별 설정

# application-local.yml
spring:
  datasource:
    url: jdbc:h2:mem:testdb
    driver-class-name: org.h2.Driver
 
# application-prod.yml  
spring:
  datasource:
    url: ${DB_URL}
    username: ${DB_USERNAME}
    password: ${DB_PASSWORD}

디버깅 체크리스트

  • 의존성 추가 확인
  • application.yml 설정 확인
  • 환경 변수 설정 확인
  • 프로파일 활성화 확인: -Dspring.profiles.active=local

일반적인 원인

  1. DB 드라이버 의존성 누락
  2. 잘못된 JDBC URL 형식
  3. 프로파일 설정 오류
  4. 환경 변수 미설정

새로운 기능 구현 예시

예시 5: 기능 구현 요청

User: JWT 기반 인증 시스템을 구현하고 싶어. Spring Security와 함께 어떻게 설정하면 될까?

AI Assistant:

구현 계획

JWT 기반 인증 시스템을 Spring Security와 통합하여 구현합니다.

구현 단계

1. 의존성 추가

dependencies {
    implementation("org.springframework.boot:spring-boot-starter-security")
    implementation("io.jsonwebtoken:jjwt-api:0.11.5")
    runtimeOnly("io.jsonwebtoken:jjwt-impl:0.11.5")
    runtimeOnly("io.jsonwebtoken:jjwt-jackson:0.11.5")
}

2. JWT 유틸리티 클래스

@Component
class JwtTokenProvider(
    @Value("\${jwt.secret}") private val secret: String,
    @Value("\${jwt.expiration}") private val expirationInMs: Long
) {
    private val key = Keys.hmacShaKeyFor(secret.toByteArray())
    
    fun generateToken(authentication: Authentication): String {
        val now = Date()
        val expiryDate = Date(now.time + expirationInMs)
        
        return Jwts.builder()
            .setSubject(authentication.name)
            .setIssuedAt(now)
            .setExpiration(expiryDate)
            .signWith(key)
            .compact()
    }
    
    fun validateToken(token: String): Boolean {
        return try {
            Jwts.parserBuilder()
                .setSigningKey(key)
                .build()
                .parseClaimsJws(token)
            true
        } catch (ex: Exception) {
            false
        }
    }
}

3. Security 설정

@Configuration
@EnableWebSecurity
class SecurityConfig(
    private val jwtAuthenticationEntryPoint: JwtAuthenticationEntryPoint,
    private val jwtAuthenticationFilter: JwtAuthenticationFilter
) {
    
    @Bean
    fun filterChain(http: HttpSecurity): SecurityFilterChain {
        http
            .csrf { it.disable() }
            .sessionManagement { 
                it.sessionCreationPolicy(SessionCreationPolicy.STATELESS) 
            }
            .authorizeHttpRequests { auth ->
                auth
                    .requestMatchers("/api/auth/**").permitAll()
                    .requestMatchers("/api/public/**").permitAll()
                    .anyRequest().authenticated()
            }
            .exceptionHandling { 
                it.authenticationEntryPoint(jwtAuthenticationEntryPoint) 
            }
            .addFilterBefore(
                jwtAuthenticationFilter, 
                UsernamePasswordAuthenticationFilter::class.java
            )
        
        return http.build()
    }
}

4. 인증 컨트롤러

@RestController
@RequestMapping("/api/auth")
class AuthController(
    private val authService: AuthService,
    private val jwtTokenProvider: JwtTokenProvider
) {
    
    @PostMapping("/login")
    suspend fun login(@RequestBody request: LoginRequest): ApiResponse<TokenResponse> {
        val authentication = authService.authenticate(
            request.email, 
            request.password
        )
        
        val token = jwtTokenProvider.generateToken(authentication)
        
        return ApiResponse.success(
            TokenResponse(
                accessToken = token,
                tokenType = "Bearer",
                expiresIn = 3600
            )
        )
    }
}

보안 고려사항

  • JWT Secret 키 안전한 보관
  • Refresh Token 구현
  • Token 블랙리스트 관리
  • HTTPS 필수 사용

참고 자료