대머리개발자

데이터 정규화 본문

개발이야기/코틀린

데이터 정규화

대머리개발자 2024. 5. 17. 09:30
728x90

데이터 모델링(?)을 하는 과정에서 동일한 데이터를 저장하는 것은 누가 봐도 비효율적이다.
 
예들들어
설문지 관련 개발한다고 하자.
 
설문지에 응답의 값을 그대로 저장한다고 하면 1~100명의 응답값이야 크게 문제가 없겠지만
1만명 이상의 응답값을 그대로 저장 한다면…….
그래도 뭐 크게 문제는 안 되겠지만
 
개발자라면..이것은 찝찝해야 한다.
그렇지 않다면 당신은 개발자가 아닐 확률이…
리팩토링 욕구가.. 아니 애초에 이렇게 설계를 하지 않겠다.
 
 
긴 문자열 응답 값을 그대로 저장하기 보단.  
별도의 테이블로 관리하고 해당 value의 pk만을 저장한다면 좋겠다.
 
요것이 바로 정규화 과정이다.
 
 
조금더 깨끗한 조금더 우아한 코드에 대한 고민을 실력이 뿜뿜!!
 
단 테이블이 추가되기  때문에 비지니스 로직이.. 상당히 귀찮아 진다.
 
 
질문에 대한 보기를 단순 딜리미티로 구분지어 하나의 컬럼에 퉁쳤지만 (주석처리한 부분)
별도의 테이블로 관리 되는 순간 질문마다 보기가 있는 경우 보기에 대한 처리를 해줘야 하기 때문에...

   fun saveForm(formQuestion: FormQuestionSaveDto): Mono<Long>{
        return formRepository.save(ModelMapper().map(formQuestion.form, Form::class.java))
            .map{
                    formQuestion.questions.forEach { questionDto ->
                        questionDto.formId = it.id
                        //questionDto.view = questionDto.viewList.joinToString(Props.SPLIT_PATTERN)
                        questionRepository.save(ModelMapper().map(questionDto, Question::class.java)).map {
                               questionDto.viewList.forEachIndexed {
                                       index, questions ->
                                            questionViewRepository.save(QuestionView(view = questions, questionId = it.id, sort = index)).subscribe()
                               }
                        }.subscribe()
                    }
            return@map it.id
        }
    }

 
여기서 재미(?) 있는 부분은.. 질문에 대한 선택지 등록은 비동기로 진행되기 때문에 만들어지는 Id가 널뛴다. ㅋ
그래도 같은 선택지안에서는 순차적으로 되면 좋을텐데... "ETC"와 "지인추천"의 ID는 순서가 바뀌어버렸네... 지꼴리!
 
응답값의 value 가 아닌 ID 값만을 저장해서.. 저장소를 스크루지 할것이다.

 
 
 
 
가져오는 부분도 마찬가지이다.  문자열을 딜리미터를 통해 배열로 바꿔주는 단순 로직에서.... 쿼리를 조회해야하는 부분으로 변경되었다.
한방 쿼리를 좋아 하지만...R2DBC...아..

fun searchQuestionByFormId(formId:Long): Mono<FormQuestionListDto>{
    return Mono.zip(
        formRepository.getFormById(formId),
        questionRepository.searchQuestionByFormId(formId)
            .flatMap { question ->
                // question.viewList = if (question.view.isNotEmpty()) question.view.split(Props.SPLIT_PATTERN) else listOf()
                questionViewRepository.searchQuestionViewsByQuestionIdOrderBySortAsc(question.id)
                    .collectList()
                    .map { questionViews ->
                        question.questionView = questionViews
                        question
                    } }
            .collectList()
    ).map {
       FormQuestionListDto( form =  it.t1, questions = it.t2)
    }
}

 
 
R2DBC... 애증인데..

728x90