인프런 커뮤니티 질문&답변

양치잘하기님의 프로필 이미지
양치잘하기

작성한 질문수

입문자를 위한 Spring Boot with Kotlin - 나만의 포트폴리오 사이트 만들기

[실습] 데이터베이스 초기화

@PostCunstructer 를 사용하는 이유가 뭔가요?

작성

·

64

0

DataIntialize 클래스에서 메서드에 포스트 컨스트럭터를 사용하셨는데

어떤 기능인지 잘 이해하지 못했습니다.

해당 클래스를 빈으로 등록하면서 같이 초기화가 이루어 지게 하는건 안되는건가요?

 

답변 2

0

정보근님의 프로필 이미지
정보근
지식공유자

안녕하세요 정보근입니다:)

 

@PostConstruct는 스프링에서 '빈 등록 및 의존성 주입을 완료한 후 초기화를 위해 딱 한 번' 수행되어야 하는 메서드에 붙이는 어노테이션입니다.

 

목적은 스프링 실행 직후 빈을 사용해서 무언가 초기화 작업을 하는 것이겠죠.

 

말씀하시 빈으로 등록한다는 건 생성자에서 초기화 로직을 넣는 것을 말씀하시는 걸까요?

 

제가 아래 코드처럼 DataInitializer를 수정해서 테스트 해보니 데이터 초기화엔 문제가 없습니다.

 

DataInitializer가 의존하는 리포지토리들은 이미 생성자가 호출되기 전에 생성이 완료되기 때문으로 보이네요.

 

그래서 결론적으로 가능은 합니다.

 

다만 클린 코드의 관점에서 생성자는 클래스의 생성에만 집중하지 못하니 단일 책임 원칙을 지키지 못하는 코드가 되겠네요.

 

또 의도적으로 DataInitializer를 한 번 더 생성하면 초기화 로직도 다시 호출될거고요.

 

스프링 의존성 주입이 완료되지 않은 상태기 때문에 예상못한 사이드 이펙트가 발생할 가능성도 있을 것 같네요.

 

이런 이유에서 말씀하신 빈 등록과 초기화는 별도로 분리하는 것이 안전한 코드라고 생각됩니다.

 

감사합니다.

 

package com.yongback.portfolio.domain

import com.yongback.portfolio.domain.constant.SkillType
import com.yongback.portfolio.domain.entity.*
import com.yongback.portfolio.domain.repository.*
import org.slf4j.LoggerFactory
import org.springframework.context.annotation.Profile
import org.springframework.stereotype.Component
import java.time.LocalDate

@Component
@Profile(value = ["default"])
class DataInitializer {

    val log = LoggerFactory.getLogger(DataInitializer::class.java)

    private lateinit var achievementRepository: AchievementRepository
    private lateinit var experienceRepository: ExperienceRepository
    private lateinit var introductionRepository: IntroductionRepository
    private lateinit var linkRepository: LinkRepository
    private lateinit var projectRepository: ProjectRepository
    private lateinit var skillRepository: SkillRepository
    private lateinit var accountRepository: AccountRepository

    constructor(
        achievementRepository: AchievementRepository,
        experienceRepository: ExperienceRepository,
        introductionRepository: IntroductionRepository,
        linkRepository: LinkRepository,
        projectRepository: ProjectRepository,
        skillRepository: SkillRepository,
        accountRepository: AccountRepository
    ) {
        this.achievementRepository = achievementRepository
        this.experienceRepository = experienceRepository
        this.introductionRepository = introductionRepository
        this.linkRepository = linkRepository
        this.projectRepository = projectRepository
        this.skillRepository = skillRepository
        this.accountRepository = accountRepository

        log.info("스프링이 실행되었습니다. 테스트 데이터를 초기화합니다.")

        // achievement 초기화
        val achievements = mutableListOf<Achievement>(
            Achievement(
                title = "2022 Catkao 해커톤 최우수상",
                description = "고양이 쇼핑몰 검색 서비스의 아키텍처, 데이터 모델링, API 개발 역할 수행",
                host = "캣카오",
                achievedDate = LocalDate.of(2022, 8, 1),
                isActive = true
            ),
            Achievement(
                title = "정보처리기사",
                description = "자료구조, 운영체제, 알고리즘, 데이터베이스 등",
                host = "한국산업인력공단",
                achievedDate = LocalDate.of(2020, 2, 2),
                isActive = true
            )
        )
        achievementRepository.saveAll(achievements)

        // introduction 초기화
        val introductions = mutableListOf<Introduction>(
            Introduction(content = "주도적으로 문제를 찾고, 해결하는 고양이입니다.", isActive = true),
            Introduction(content = "기술을 위한 기술이 아닌, 비즈니스 문제를 풀기 위한 기술을 추구합니다.", isActive = true),
            Introduction(content = "기존 소스를 리팩토링하여 더 좋은 구조로 개선하는 작업을 좋아합니다.", isActive = true)
        )
        introductionRepository.saveAll(introductions)

        // link 초기화
        val links = mutableListOf<Link>(
            Link(name = "Github", content = "https://github.com/infomuscle", isActive = true),
            Link(name = "Linkedin", content = "https://www.linkedin.com/in/bokeunjeong", isActive = true),
        )
        linkRepository.saveAll(links)

        // experience / experience_detail 초기화
        val experience1 = Experience(
            title = "캣홀릭대학교(CatHolic Univ.)",
            description = "컴퓨터공학 전공",
            startYear = 2018,
            startMonth = 9,
            endYear = 2022,
            endMonth = 8,
            isActive = true
        )
        experience1.addDetails(
            mutableListOf(
                ExperienceDetail(content = "GPA 4.3/4.5", isActive = true),
                ExperienceDetail(content = "소프트웨어 연구 학회 활동", isActive = true)
            )
        )
        val experience2 = Experience(
            title = "주식회사 캣카오(Catkao Corp.)",
            description = "소셜서비스팀 백엔드 개발자",
            startYear = 2022,
            startMonth = 9,
            endYear = null,
            endMonth = null,
            isActive = true
        )
        experience2.addDetails(
            mutableListOf(
                ExperienceDetail(content = "유기묘 위치 공유 서비스 개발", isActive = true),
                ExperienceDetail(content = "신입 교육 프로그램 우수상 수상", isActive = true)
            )
        )
        experienceRepository.saveAll(mutableListOf(experience1, experience2))

        // skill 초기화
        val java = Skill(name = "Java", type = SkillType.LANGUAGE.name, isActive = true)
        val kotlin = Skill(name = "Kotlin", type = SkillType.LANGUAGE.name, isActive = true)
        val python = Skill(name = "Python", type = SkillType.LANGUAGE.name, isActive = true)
        val spring = Skill(name = "Spring", type = SkillType.FRAMEWORK.name, isActive = true)
        val django = Skill(name = "Django", type = SkillType.FRAMEWORK.name, isActive = true)
        val mysql = Skill(name = "MySQL", type = SkillType.DATABASE.name, isActive = true)
        val redis = Skill(name = "Redis", type = SkillType.DATABASE.name, isActive = true)
        val kafka = Skill(name = "Kafka", type = SkillType.TOOL.name, isActive = true)
        skillRepository.saveAll(mutableListOf(java, kotlin, python, spring, django, mysql, redis, kafka))

        // project / project_detail / project_skill 초기화

        val project1 = Project(
            name = "유기묘 발견 정보 공유 서비스",
            description = "유기묘 위치의 실시간 공유, 임시보호까지 연결해주는 서비스. 구글 맵스를 연동하여 유기묘 위치 정보를 직관적으로 파악할 수 있도록 하는 사용자 경험 개선 작업.",
            startYear = 2022,
            startMonth = 9,
            endYear = 2022,
            endMonth = 12,
            isActive = true
        )
        project1.addDetails(
            mutableListOf(
                ProjectDetail(content = "구글 맵스를 활용한 유기묘 발견 지역 정보 제공 API 개발", url = null, isActive = true),
                ProjectDetail(content = "Redis 적용하여 인기 게시글의 조회 속도 1.5초 → 0.5초로 개선", url = null, isActive = true)
            )
        )
        project1.skills.addAll(
            mutableListOf(
                ProjectSkill(project = project1, skill = java),
                ProjectSkill(project = project1, skill = spring),
                ProjectSkill(project = project1, skill = mysql),
                ProjectSkill(project = project1, skill = redis)
            )
        )
        val project2 = Project(
            name = "반려동물 홈 카메라 움직임 감지 분석 모듈",
            description = "카메라에서 서버로 전달되는 신호를 분석하여 움직임이 감지될 경우 클라이언트에게 알림 발송 작업.",
            startYear = 2022,
            startMonth = 12,
            endYear = null,
            endMonth = null,
            isActive = true
        )
        project2.addDetails(
            mutableListOf(
                ProjectDetail(content = "PIL(Pillow) 활용하여 이미지 분석 기능 개발", url = null, isActive = true),
                ProjectDetail(content = "알림 발송을 비동기 처리하여 이미지 분석 - 알림 발송 기능간 의존도 감소", url = null, isActive = true),
                ProjectDetail(content = "Github Repository", url = "https://github.com/infomuscle", isActive = true)
            )
        )
        project2.skills.addAll(
            mutableListOf(
                ProjectSkill(project = project2, skill = python),
                ProjectSkill(project = project2, skill = django),
                ProjectSkill(project = project2, skill = kafka)
            )
        )
        projectRepository.saveAll(mutableListOf(project1, project2))

        val account = Account(
            loginId = "admin1",
            pw = "\$2a\$10\$BWi6SLqZRJyVvJyufjTtHeYXNNhpNY9rxaVl9fBOE.1t3QF98B.cO"
        )
        accountRepository.save(account)

    }
}

 

이해했습니다 감사합니다.

0

안녕하세요, 인프런 AI 인턴입니다. 양치잘하기님.

@PostConstruct는 Java에서 제공하는 어노테이션으로, 스프링의 빈 초기화 과정에서 동작하는 메서드를 지정하는 역할을 합니다. 이 어노테이션이 붙은 메서드는 해당 빈이 스프링에 의해 DI(Dependency Injection)가 완료된 후에 자동적으로 호출됩니다.

즉, 객체가 생성되고 모든 의존성이 주입된 이후, 어떤 초기화 작업이 필요할 때 사용합니다. 이런 초기화 작업은 보통 데이터 설정, 리소스 로딩 등을 포함할 수 있습니다.

스프링에서는 빈이 생성될 때 생성자 호출 -> 의존성 주입 -> 초기화의 순서로 진행되기 때문에, @PostConstruct를 사용하면 이 초기화 과정에서 적절하게 필요한 초기설정을 수행할 수 있습니다.

기존의 Java main 메서드와 같이 직접 호출하는 것이 아니라, 스프링 컨테이너에 의해 관리되는 빈의 라이프사이클에 맞추어 호출할 수 있는 점에서 유용합니다.

이보다 더 깊은 이해가 필요하시다면 아래의 링크들을 참고해 보세요:

저는 질문자님의 이해를 돕고자 지식공유자님의 기존 답변을 참고하여 유사한 답변 링크를 추천해드리고 있습니다. 현재 베타 기능으로 답변이 만족스럽지 않을 수 있는 점 양해 부탁드립니다. 🙏 추가적으로 궁금한 점이 있으시면, 이어서 질문해 주세요. 곧 지식공유자께서 답변해 주실 것입니다.

양치잘하기님의 프로필 이미지
양치잘하기

작성한 질문수

질문하기