소개
안녕하세요. 지식공유자 설동민입니다.
현재 카카오 백엔드 개발자로 근무하고 있으며, 복잡한 문제를 명쾌하게 풀어내는걸 좋아합니다.
경영학부 비전공자 출신으로, 다양한 OpenSource들에 대한 분석과 여러 기술적인 경험들을 통해
웹의 전반적인 기술을 학습하였습니다. OOP와 SQL, 이 어울리지 않는 두가지를 제일 좋아합니다.
Github: https://github.com/SightStudio
이력
2021. 12 ~ 현재 : 카카오 백엔드 개발
2020.12 ~ 2021.12: 전) 이스트소프트 백엔드 개발
강의
수강평
- 실전 jOOQ! Type Safe SQL with Java
- 실전 jOOQ! Type Safe SQL with Java
- 실전 jOOQ! Type Safe SQL with Java
- 실전 jOOQ! Type Safe SQL with Java
게시글
질문&답변
FilmWithActor로 fetch 시 select와 생성자의 매개변수 순서가 다를 경우 map이 정상적으로 이루어 지지않는 케이스
하디님 안녕하세요 강의에서는 일부 row를 그룹핑해서 다른 객체로 만들 수 있다는걸 보여주기 위해서 위의 방식으로 소개했었는데요. 테이블 자체를 select하는 경우에는 Tables as SelectField 방식으로 처리하는것을 추천드립니다. 🙂 테이블 객체를 field로 사용하는 경우 아래와 같이 mapping 할 때 테이블 명으로 매핑할 클래스 필드명을 찾아서 매칭해주기 때문에 순서를 고려하지 않으셔도 됩니다. 예시 1) 위의 "Table as SelectField"를 사용한 경우 (사진) 예시 2) 강의의 DSL.row를 사용한 경우 (사진) 강의오픈 후에 이부분 설명이 아쉽다고 생각해서 강의노트에 추가했었는데요.이부분은 추후에 다른분들도 어떻게 잘 알 수 있을지 고민해보도록 하겠습니다. 테이블이 아닌경우 DSL.row()와 매퍼를 적절히 사용해주시면 됩니다.dslContext.select( DSL.row( FILM.FILM_ID, FILM.TITLE, FILM.DESCRIPTION ).mapping(SimpleFilmInfo::new) ) .from(FILM) .where(FILM.FILM_ID.eq(id)) .fetchOneInto(SimpleFilmInfoWrapper.class); // class SimpleFilmInfoWrapper { private SimpleFilmInfo simpleFilmInfo; }
- 1
- 1
- 61
질문&답변
from절 subquery table filed nullable 처리
안녕하세요. 어떤상황인지 정확히 파악이 안되서 답변드리기가 어려운데요.관련된 예시 코드나 사진이 있을까요?
- 1
- 1
- 88
질문&답변
kotlin mapping error
최종 정리본 안녕하세요. 이부분 조금 이해가 안가서 jOOQ 쪽에 이슈를 올렸었는데요.창시자 답변을 받아서 공유합니다. https://github.com/jOOQ/jOOQ/issues/17157 원인은 DefaultRecordMapper의 아래 두 옵션때문에 다르게 동작하는것으로 확인되었습니다.자바: isMapConstructorParameterNames (기본값: false)코틀린: isMapConstructorParameterNamesInKotlin (기본값: true) 해당 옵션은 생성자가 존재 할 경우 생성자의 변수명을 리플랙션으로 읽어서 sql의 alias와 매핑해주는 옵션입니다. 자바의 default 값이 false인 이유는 해당 옵션이 자바 8 이후 도입되어서 하위 의존성을 위해 false로 되어있기 때문에 그렇습니다. 다만 implicit path join 과 explicit path join은 이렇게되면 sql의 alias가 "alias_xxxx" 형식으로 나타나기 때문에 매핑이 안되고 에러가 발생하는 것이였습니다. (생성되는 SQL에서 확인 할 수 있습니다.) isMapConstructorParameterNamesInKotlin 옵션을 false로 처리하면 자바처럼 동작하겠지만,이 옵션을 켤지말지에 대한 판단이 필요합니다. (사실 코틀린이라면 이 옵션을 키는게 좋다고 생각합니다. 필드 선언부가 생성자와 같이 있기 때문에, 개인적인 의견입니다.) @Configuration class JooqConfig { @Bean fun jooqDefaultConfigurationCustomizer(): DefaultConfigurationCustomizer { return DefaultConfigurationCustomizer { c -> c.settings() // where 절 없이 delete, update 실행 금지 .withExecuteDeleteWithoutWhere(ExecuteWithoutWhere.THROW) .withExecuteUpdateWithoutWhere(ExecuteWithoutWhere.THROW) // -- 이부분 .withMapConstructorParameterNamesInKotlin(false) // implicit join 사용 금지 .withRenderImplicitJoinType(RenderImplicitJoinType.THROW) // schema 미포함 .withRenderSchema(false) } } } 만약 옵션을 키고 Explicit Path Join을 사용하고 싶다면 현재는 아래처럼 Path에 강제로 alias를 주시면 됩니다.fun findFilmWithActorsListLast(): List { val FILM_ACTOR = FILM.filmActor.`as`("filmActor") val ACTOR = FILM.filmActor.actor.`as`("actor") return dslContext .select( FILM, FILM_ACTOR, ACTOR, ).from(FILM) .join(FILM_ACTOR) .join(ACTOR) .fetchInto(FilmWithActor::class.java) } 창시자도 이부분이 애매한것같아 alias를 주지 않아도 되도록 개선해보겠다고 하니 지켜보면 될듯합니다. 감사합니다. (저도 이런 세팅이 존재하는지 몰랐네요 ㅎㅎ)https://github.com/jOOQ/jOOQ/issues/17170
- 0
- 4
- 311
질문&답변
R2DBC 관련해서 질문 드립니다.
안녕하세요. 인프런에 jOOQ 강의를 출시한 강사입니다.다른분들 질문 답변드리다가 이 질문이 보여서 슬쩍... 의견드리고 갑니다. 본 강의 강사님의 생각이 다르실 수도 있지만. 제가 생각하기엔jOOQ 자체가 생각보다 잘 알려지지 않은 기술이기 때문에 강의의 내용과 동떨어 질 수 있음jOOQ는 다른 데이터베이스 접근 기술보다 초기 세팅비용이 큼 (마찬가지로 이때문에 강의의 내용과 멀어질 수 있음) jOOQ도 R2DBC 환경에서 기존 기능의 100%를 지원하지 못함 (이 질문에서도 확인 할 수 있습니다.)이런 이유 때문에 사용 안하셨을것이라고 조심스럽게 의견드려봅니다. 감사합니다 🙂
- 0
- 3
- 220
질문&답변
kotlin mapping error
안녕하세요. 디버깅을 좀 해보니 이야기가 길어질것같아 답변먼저 작성하고 설명은 밑에 하도록 하겠습니다. 테이블의 모든 필드를 가져오려면 알려주신 방법처럼 테이블 인스턴스를 인자로 주는게 제일 나아보입니다. fun findFilmWithActorList(page: Long, size: Long): List { val FILM_ACTOR = JFilmActor.FILM_ACTOR val ACTOR = JActor.ACTOR return dslContext .select( FILM, FILM_ACTOR, ACTOR ) .from(FILM) .join(FILM_ACTOR).on(FILM.FILM_ID.eq(FILM_ACTOR.FILM_ID)) .join(ACTOR).on(ACTOR.ACTOR_ID.eq(FILM_ACTOR.ACTOR_ID)) .offset((page - 1) * size) .limit(size) .fetchInto(FilmWithActor::class.java) } 여기서 만약, SELECT한 필드중 일부를 객체로 매핑하려면 아래처럼 하시면 됩니다. data class FilmWithActorName(val film: Film, val filmActor: FilmActor, val actorName: ActorName) data class ActorName(val firstName: String?, val lastName: String?) fun findFilmWithActorsListLast( page: Long, pageSize: Long ): List { val FILM_ACTOR = JFilmActor.FILM_ACTOR val ACTOR = JActor.ACTOR return dslContext .select( FILM, FILM_ACTOR, row( ACTOR.FIRST_NAME, ACTOR.LAST_NAME ).mapping(::ActorName).`as`("actor_name") // 문자열이 부담된다면 .`as`(ActorName::class.simpleName) 로도 가능 ).from(FILM) .join(FILM_ACTOR).on(FILM.FILM_ID.eq(FILM_ACTOR.FILM_ID)) .join(ACTOR).on(FILM_ACTOR.ACTOR_ID.eq(ACTOR.ACTOR_ID)) .limit(pageSize) .offset((page - 1) * pageSize) .fetchInto(FilmWithActorName::class.java) } 해설 테이블 인스턴스를 인자로 넘기는 것과 DSL.row(*TABLE.fields()) 를 인자로 넘기는 것의 차이는 테이블로부터 생성된 레코드인지 아니면 커스텀하게 생성된 레코드 인지 여부입니다. 테이블 객체를 넘기면 clllickme 님이 올려주신 fetch 후 map 하는 작업을 내부적으로 진행합니다. 다만, 코틀린에서는 커스텀하게 생성된 레코드들에 대해서 매칭을 하지 못해 내부적으로 null을 할당하다 에러가 발생한 것으로 보입니다.(자세한 동작을 보고싶다면. jOOQ 내부의 DefaultRecordMapper의 ImmutablePOJOMapper를 보시면 됩니다.) 그래도 이부분은 크게 문제 없어보입니다. 테이블 컬럼 전체를 select 하는게 아니면 보통 제가 올린 답변의 예제처럼 .mapping으로 매핑 할 객체를 명시하기 때문에 답변대로 진행하면 큰 문제 없어보입니다. (사진)(사진)
- 0
- 4
- 311
질문&답변
kotlin mapping error
안녕하세요 제가 이번주 조금 바빠서 확인을 못했네요이번주 안에 답변드리도록 하겠습니다. :)
- 0
- 4
- 311
질문&답변
db column은 not null로 되어 있는데 kotlin에서 pojo가 다 nullable로 생성되요.
문서: https://www.jooq.org/doc/latest/manual/code-generation/kotlingenerator/ 버터플라잉교육용 님 안녕하세요.해당 부분 KotlinGenerator에서 옵션으로 지원되고 있습니다.다만 원하시는 옵션의 default가 false여서 nullable로 처리되어있습니다.KotlinNotNullPojoAttributes 옵션을 true로 변경하시면 됩니다. 해당 부분 강의자료에 추가해놓았으니 참고하시면 좋을듯합니다.감사합니다.https://github.com/SightStudio/jOOQ-inflearn/tree/main/issue_1314279_jooq-with-kotlin다만 PK는 원하는 대로 not null 이 되지 않을 수 있는데요. 관련 이슈https://github.com/jOOQ/jOOQ/issues/16007 이 부분이 마음에 들지 않는다면 이런 방법도 있습니다.https://www.jooq.org/doc/latest/manual/code-generation/codegen-version-control/ POJO는 일회성 generator용도로만 생성하고 버전관리를 하는 방식입니다.이 문서를 보면 POJO만 별도로 다른 패키지에서 생성되게 할 수 있습니다. 이를 통해 POJO를 다른 곳에서 생성되게하여 git으로 버전관리 하게 할 수 있습니다. 이렇게 하면 우리가 POJO 관리하게되니 좀 더 편하게 사용 할 수 있습니다. 대신 테이블에 대한 변경도 우리가 기존 마이바티스에서 했듯이 수동 필드들을 관리줘야합니다. (트레이드 오프)
- 0
- 1
- 242
질문&답변
하나의 디비를 여러 백엔드 서버가 필요로 할 때, flyway 관리
해당질문 완료처리하도록 하겠습니다. 궁금한 부분있으면 추가적으로 댓글 부탁드려요
- 1
- 5
- 485
질문&답변
하나의 디비를 여러 백엔드 서버가 필요로 할 때, flyway 관리
안녕하세요 해당 질문 답변드립니다. 스프링에서 CICD 단계 때 flyway로 데브나 프로덕션 단계 때 마이그레이션 적용 가능여부해당 방식 여러가지 방법으로 가능합니다. 다만 이 방식 자체를 추천하지 않습니다.maven 이나, gradle 에서 빌드 완료 시 실행 할 수도 있고, 배포 후 스프링에서 이렇게 실행을 할 수 도 있지만이런 방식으로 DDL 자체를 dev나 prod에서 자동으로 실행하는건 너무나도 위험합니다. DDL은 트랜잭션이 지원되지 않습니다.실수로 빌드가 완료 된 후 DDL을 실행했는데 중간에 오타가 있다면 그 부분은 결국 또 수기로 처리해야합니다. 개발 페이즈에 만들었던 DDL을 운영에 그대로 반영하면 안되는 경우도 꽤 있습니다.배포와는 별개로 사용자가 적은 시간대에 인덱스를 추가하거나, online DDL을 위해서 쿼리가 변경되서 반영되어야하는 경우가 예시 중 하나입니다. 데이터가 유실되거나 장애를 발생시킬 경우도 존재하기 때문에 운영 DDL은 자동화하면 안됩니다. 프로젝트(레포지토리)가 4개 경우에 code로 DDL 관리4개의 프로젝트가 같은 DB를 바라보고있는걸로 보이는데요.RDBMS의 role로 관리되고 있다면 해당 role 에 맞춰서 프로젝트별로 관리하면 되고, DDL을 코드로 관리하기 전에 4개 프로젝트 모두 모든 DB 테이블에 접근 할 수 있나요?만약 그렇다면 이부분부터 문제가 있다고 생각합니다.만약 논리적인 구분없이 4개의 프로젝트 모두 DB 테이블에 접근이 가능하다면4개의 프로젝트를 하나의 모노레포로 만들고 하위에 공통 모듈을 두어 거기서 DSL을 만들어 관리하게 하는 방법이 제일 적절해보입니다.
- 1
- 5
- 485
질문&답변
하나의 디비를 여러 백엔드 서버가 필요로 할 때, flyway 관리
오늘안에 답변드리도록 하겠습니다.
- 1
- 5
- 485