
백엔드 코틀린 개발하기 2주차 모르는 개념 정리
Domain 개발하기
데이터 초기화
@Component // 스프링 빈으로 등록
어떤 클래스를 빈으로 등록할지 알려주는 어노테이션
📌 @Component
란?
@Component
는 Spring Framework에서 빈(Bean)으로 등록하기 위한 기본적인 애너테이션입니다.
즉, Spring이 해당 클래스를 자동으로 스프링 컨테이너(IOC 컨테이너)에 등록하도록 해줍니다.
🚀 @Component
사용 방법
@Component
class MyService {
fun doSomething() {
println("MyService is working!")
}
}
@Component
를 사용하면MyService
클래스가 Spring Bean으로 등록됨.다른 곳에서
@Autowired
또는 생성자 주입을 통해 사용할 수 있음.
@Service
class SomeClass(private val myService: MyService) {
fun execute() {
myService.doSomething()
}
}
SomeClass
에서MyService
를 자동으로 주입받아 사용 가능🎯
@Component
vs 기타 빈 애너테이션
Spring에서는 @Component
외에도 특정한 목적에 맞는 다른 애너테이션들이 있습니다.
애너테이션 설명 @Component
가장 기본적인 빈 등록 애너테이션 @Service
비즈니스 로직을 처리하는 클래스에 사용 @Repository
데이터베이스 작업(DAO, Repository)에 사용 @Controller
Spring MVC 컨트롤러(웹 요청 처리)에 사용
💡 실제 동작은 동일하지만, 역할에 맞게 애너테이션을 사용하는 것이 가독성과 유지보수성을 높이는 데 도움이 됩니다.
📌 @Component
를 어디서 쓰면 좋을까?
✅ 사용하면 좋은 경우
특정한 역할이 없는 일반적인 Bean을 만들 때 (
Utility Class
,Helper
,Config
)다른 서비스, 레포지토리와는 다르게 특별한 역할이 없는 경우
❌ 다른 애너테이션을 쓰는 것이 좋은 경우
비즈니스 로직을 처리하는 경우 →
@Service
데이터 액세스 관련 클래스 →
@Repository
웹 요청을 처리하는 컨트롤러 →
@Controller
🔥 결론
@Component
는 Spring Bean을 등록하는 가장 기본적인 애너테이션.역할이 명확한 경우
@Service
,@Repository
,@Controller
를 사용하는 것이 더 가독성이 좋음.주로 일반적인 유틸리티 클래스나 특정한 계층에 속하지 않는 클래스에서 사용됨. 🚀
📌
@PostConstruct
란?
@PostConstruct
는 Spring에서 빈(Bean)이 생성된 후 자동으로 실행되는 메서드를 지정하는 애너테이션입니다.
즉, 스프링 컨테이너가 해당 빈을 생성하고 의존성 주입(DI)이 완료된 후 한 번 실행되는 초기화 메서드를 정의할 때 사용된다.
🚀 @PostConstruct
기본 사용 예시
@Component
class MyService {
@PostConstruct
fun initializeData() {
println("MyService 초기화 중...")
// 초기 데이터 설정 로직
}
}
✅ 실행 흐름:
MyService
가 스프링 컨테이너에 의해 빈(Bean)으로 생성됨.의존성 주입(DI)이 완료됨.
@PostConstruct
가 붙은initializeData()
메서드가 자동 실행됨.
💡 @PostConstruct
의 주요 특징
Spring Bean이 생성된 후, 단 한 번만 실행됨.
의존성 주입이 완료된 후 실행되므로, 다른 빈을 사용할 수 있음.
애플리케이션이 실행될 때 한 번만 초기화해야 하는 작업에 적합.
🎯 @PostConstruct
가 유용한 경우
사용 예시 설명 초기 데이터 설정 DB에서 기본 데이터를 불러오거나, 캐시를 로딩할 때 외부 API 연동 애플리케이션 시작 시 특정 API와 통신해야 할 때 리소스 초기화 특정 설정 파일을 읽거나, 초기 환경을 세팅할 때
📌 데이터 초기화 예시
@Service
class DataService(private val repository: MyRepository) {
@PostConstruct
fun initializeDatabase() {
if (repository.count() == 0L) {
repository.saveAll(listOf(MyEntity("데이터1"), MyEntity("데이터2")))
println("초기 데이터 삽입 완료")
}
}
}
✅ 데이터베이스에 초기 데이터를 넣어야 할 때 유용함.
⚠ 주의할 점
❌ 비동기 작업 (@Async
)과 함께 사용하면 안 됨
@PostConstruct
는 빈 초기화 이후 즉시 실행되므로 비동기 메서드와 충돌 가능.대안:
ApplicationRunner
또는CommandLineRunner
를 활용.
@Bean
fun initRunner() = ApplicationRunner {
println("애플리케이션 실행 후 초기화 코드 실행")
}
🔥 결론
@PostConstruct
는 Spring Bean이 생성된 후 단 한 번 실행되는 초기화 메서드.초기 데이터 설정, API 호출, 설정값 초기화 등에 유용.
비동기 작업과 함께 사용하면 예상치 못한 문제가 발생할 수 있음.
→ 이럴 때는ApplicationRunner
를 고려하는 것이 좋음. 🚀📌
achievementRepository.saveAll(achievements)
데이터가 없는데도 되는 이유
Spring Data JPA의 saveAll()
메서드는 비어있는 리스트(List<T>
)를 전달해도 오류 없이 실행되도록 설계되어 있다.
즉,
achievements
리스트가 비어있다면 아무런 데이터도 저장되지 않고, 그냥 정상적으로 메서드가 종료된다.
🚀 saveAll()
동작 방식
achievementRepository.saveAll(emptyList()) // 데이터가 없어도 실행됨
✔ 내부적으로 실행되는 로직
비어있는 리스트(
emptyList<T>()
)가 들어오면, 아무 동작 없이 바로 반환.데이터가 있으면 각각의 엔티티를
save()
메서드를 이용해 Batch Insert 또는 개별 저장 수행.트랜잭션이 있으면 한 번에 처리되고, 없으면 개별 실행됨.
🎯 saveAll()
이 실행될 때 두 가지 경우
1⃣ 데이터가 있을 때
val achievements = listOf(
Achievement(name = "Level 1", score = 10),
Achievement(name = "Level 2", score = 20)
)
achievementRepository.saveAll(achievements) // DB에 저장됨
saveAll()
은 리스트에 있는 엔티티들을 한꺼번에 저장함.
2⃣ 데이터가 없을 때
val emptyList = emptyList<Achievement>()
achievementRepository.saveAll(emptyList) // 아무 일도 일어나지 않음
saveAll()
은 빈 리스트가 들어오면 아무런 작업도 하지 않고 그냥 종료됨.SQL 쿼리가 실행되지 않으며, 오류도 발생하지 않음.
🔥 결론
✅ saveAll(emptyList())
는 아무런 데이터도 저장하지 않고 그냥 종료되도록 설계됨.
✅ 데이터가 없을 경우에도 오류 없이 정상 동작함.
✅ Spring Data JPA는 이런 경우를 고려하여 예외를 던지지 않음.
📌 즉, 데이터가 없어도 saveAll()
이 실행되지만, 실제로 DB에는 아무런 변경이 일어나지 않는 것! 🚀
Kotlin에서 fun
의 의미
Kotlin에서
fun
키워드는 함수를 정의할 때 사용되는 키워드
Java의public List<Skill> findAllByIsActive(boolean isActive)
와 같은 역할을 한다.
🚀 fun findAllByIsActive(isActive: Boolean): List<Skill>
// select * from skill where is_active = :isActive
fun findAllByIsActive(isActive: Boolean): List<Skill>
✅ 설명
findAllByIsActive(isActive: Boolean)
:isActive
값이true
또는false
인 데이터를 조회하는 메서드.SQL 변환:
SELECT * FROM skill WHERE is_active = :isActive
반환 타입:
List<Skill>
→ 여러 개의Skill
객체 리스트를 반환.
🔹 사용 예시
val activeSkills: List<Skill> = skillRepository.findAllByIsActive(true)
println(activeSkills) // 활성화된 스킬 목록 출력
🚀 fun findByNameIgnoreCaseAndType(name: String, type: SkillType): Optional<Skill>
// select * from skill where lower(name) = lower(:name) and type = :type
fun findByNameIgnoreCaseAndType(name: String, type: SkillType): Optional<Skill>
✅ 설명
findByNameIgnoreCaseAndType(name: String, type: SkillType)
:이름(name) 대소문자 구분 없이 검색하고,
타입(type)이 일치하는 데이터를 조회.
SQL 변환:
SELECT * FROM skill WHERE lower(name) = lower(:name) AND type = :type
반환 타입:
Optional<Skill>
→ 조회된 값이 있을 수도 있고, 없을 수도 있음.
🔹 사용 예시
val skill: Optional<Skill> = skillRepository.findByNameIgnoreCaseAndType("java", SkillType.PROGRAMMING)
if (skill.isPresent) {
println("Skill found: ${skill.get()}")
} else {
println("Skill not found")
}
🔥 결론
Kotlin에서
fun
은 함수 선언 키워드.findAllByIsActive(isActive: Boolean)
: 활성화된(is_active
) 데이터를 리스트로 반환.findByNameIgnoreCaseAndType(name, type)
: 대소문자 구분 없이 특정 이름과 타입을 가진 데이터를 조회.Spring Data JPA는 메서드 이름을 기반으로 자동으로 SQL을 생성하여 실행함. 🚀
회고
사실 왕복 3시간 거리 회사를 다니고 지금 커피챗, 친구들 만나기, 스터디 2개까지 해서 솔직히 버거워서 밀렸음 그래도 일단 과제를 내고자 하는 의지로 내는 중...
댓글을 작성해보세요.