게시글
질문&답변
2024.11.10
영속성 컨텍스트 1차캐시 질문
안녕하세요 영욱님! 🙂 정말 기가 막힌 포인트를 찾아내셨군요...! 👍 결론부터 빠르게 말씀드리면, 이것은 OSIV 라는 옵션 때문입니다. Open-Session-In-View 옵션은 기본적으로 true로 설정되어 있는데spring: jpa: open-in-view: false를 이용해 false로 만들 수 있고요 실무에서 권장되는 값은 false 입니다. 그럼 이제 OSIV 옵션이 무엇인지 설명을 드리면,만약 이 값이 true로 설정되어 있다면, 트랜잭션이 열러 영속성 컨텍스트가 한 번 로딩되면, 해당 요청이 완전히 종료될 때까지 영속성 컨텍스트를 유지하게됩니다.때문에 위 경우는 처음 로딩된 findById() 에서 Repository 내부에 있는 트랜잭션 실행 -> OSIV 설정에 의해 영속성 컨텍스트가 유지되어 버림 -> Service 종료시점까지 (심지어 Controller를 완전히 벗어나 요청이 끝날 때까지) 영속성 컨텍스트가 남아 있게 됩니다. 이런 설정이 존재하는 이유는 과거의 "템플릿 엔진"을 사용했기 때문인데요요즘에는 react.js 혹은 next.js와 같은 FE 영역과 RESTful API BE 영역이 완전히 구분되지만, 옛날에는 서버가 직접 HTML을 브라우저에 반환하는 기법을 사용했습니다. 그리고 HTML을 사용자마다 다르게 보여주려면, 안녕하세요 ??? 님 처럼 ??? 이란 빈 칸을 뚫어두고 DB에서 조회해온 값을 여기에 넣어주게 되었죠.이때 DB에서 조회해온 값 (= Service와 Repository를 거쳐 받아온 값) 을 템플릿 엔진에서 조금 더 적극적으로 사용하기 위한 (= 빈칸에서 Entity를 이리저리 사용하기 위한) 옵션이 OSIV 였습니다. 현재 실무에서 OSIV를 false로 권장하는 이유는, 대부분 REST API를 만들기 때문에 OSIV 처럼 암시적으로 영속성 컨텍스트가 오래 유지되며 생기는 문제들을 원천 차단하기 위해서입니다. OSIV를 잘못 사용하게 되면 Controller에서 Entity에 넣은 값이 dirty check 등으로 저장되어 버릴 수도 있고, 영속성 컨텍스트를 오래 유지하며 시스템 자원이 낭비될 수도 있기 때문이죠. 이런 부분은 첫 서버를 배우는데 함께 설명드리기에는 어려운 내용이라 이번 강의에서는 제외되어 있습니다. 🙂 열심히 들어 주셔서 감사합니다. 또 궁금한 점 편하게 질문 올려주세요! 감사합니다. 🙇
- 1
- 2
- 25
질문&답변
2024.11.10
ddl-auto 관련 질문.
안녕하세요 영욱님! 🙂 좋은 질문 감사드립니다. 결론부터 말씀드리면 validate 과정에서는 JPA에 있는 column을 기준으로 DB에 해당 column이 존재하는지를 확인하고 있습니다. 때문에 꼭 nullable 필드가 아니더라도 DB에 column이 더 많다면 검증 과정에서 애플리케이션 부팅 과정을 중단하지 않을겁니다. 또한 Column 에 들어가는 속성 값 중 type과 같이 조금 중요한(?) 일부 설정은 함께 검증해주는 것으로 알고 있는데 그 외 설정들은 검증하지 않는 경우가 있습니다.실제 검증 로직은 AbstractSchemaValidator 에서 확인하실 수 있는데요링크 : https://github.com/hibernate/hibernate-orm/blob/main/hibernate-core/src/main/java/org/hibernate/tool/schema/internal/AbstractSchemaValidator.java#L126(table.getColumns 에서 가져오는 column이 JPA Entity의 column입니다. 물리 테이블을 기준으로 비교하고 있지 않죠.) 저는 개인적으로 validate 를 사용하지 않고 직접 DDL 변경을 챙겨주는 편을 선호합니다. 🙂그 이유는 확인해보신 것처럼 validate 자체가 완벽하지 않을 뿐더러 DB schema를 완벽하게 관려하려면 (비록 MySQL 8.0부터는 유료이지만) flyway라는 툴이 조금 더 나을 수 있기 때문이죠. flyway보다 직접 DDL 변경을 챙겨주는 것을 선호하는 이유는 flyway를 사용함으로써 flyway 규칙을 따라야만 해서 오히려 유연함이 떨어지는 경우가 종종 있고, 유료 솔루션을 사용하는 것에 대해 조직 단위 설득이 어려운 이유도 있습니다. 이렇게 직접 옵션을 꼼꼼하게 확인해보시다니 대단하시네요~! 😊또 궁금한 점 있으시면 편하게 질문 남겨주세요. 감사합니다. 🙇
- 1
- 2
- 22
질문&답변
2024.11.09
[28000][1045] Access denied for user 'root'@'localhost' (using password: YES)
안녕하세요! kangaroo125님! 🙂 해당 오류는 일반적으로 비밀번호가 틀렸다는 오류입니다. Access denied for user 'root' 혹시 비밀번호를 올바르게 입력하셨는지 확인해보시면 좋을 것 같습니다.감사합니다. 🙇
- 0
- 2
- 28
질문&답변
2024.11.07
GitHub에 application설정 내용 push
안녕하세요 재성님! 질문 주셔서 감사합니다.결론부터 말쓰드리면, "gitignore을 통해서 push하지 않도록 하지 않는" 이상 다른 방법은 없습니다! 🥲 그래서 비밀번호를 설정할 때 1234 같은 단순한 비밀번호로 설정하라고 말씀 드렸던 거에요!! 실무에서는 보통application.yml 같은 곳에도 비밀번호를 두지 않고 AWS SecretsManager 같은 외부 솔루션을 통해 아예 비밀번호 자체를 모르게끔 처리하는 편입니다. 감사합니다. 🙇
- 0
- 3
- 28
질문&답변
2024.11.07
간단 질문.. join() vs delay()
안녕하세요 보키님! 🙂 질문 주셔서 감사합니다.아래 1번, 2번 방법 중에 어떤 차이가 있고, 코루틴을 현업에서 쓸 때는 어떤 방법으로 보통 사용하게되나요?오...! AI 인턴의 사용이 매우 정확하군요- join()을 사용하는 것이 명확하고 안정적입니다. 원하는 코루틴이 완료될 때까지 기다린다는 의미를 명확히 전달하기 때문입니다.- delay()는 명시적인 요구사항에 따라 T를 기다리고 싶을 때 사용되지만, 보통 정확한 완료를 보장하지 않기 때문에 join()을 대체하는 용도로 사용되지는 않습니다.delay() 는 임의의 시간을 멈추는 것이기 때문에 join() 을 대체하는 용도로는 사용되지 않습니다. 그리고 강의를 아직 다 보지는 않았는데.. RxJava, WebFlux, Virutal Thread, Coroutine 등을 어떤 영역에서 활용하는지 궁금합니다Network, File I/O, DB CRUD, External API call, etc...본 강의는 코루틴 강의이기 때문에 RxJava, Webflux, 가상 스레드 등에 대해서는 설명하고 있지 않습니다. 그렇지만 여기서 간단히 설명드리면 RxJava, Coroutine은 non-blocking 작업을 처리하기 위한 코드 스타일이고요~ WebFlux는 non-blocking을 활용할 수 있도록 만들어진 framework, Virtual Thred는 blocking I/O를 non-blocking 하게 처리할 수 있도록 해주는 기법 입니다.결국 넷 모두 non-blocking I/O를 핸들링 하기 위한 기술이기에 I/O가 발생하는 곳이라면 어디든 적용 가능합니다. 🙂 그리고 음.. 2022년도쯤에는 비동기트랜잭션이 하이버네이트쪽에서 잘 지원 안해줘서 r2dbc를 사용하다가 취소한 곳이 있다고도 얼핏 들은것같은데 현재는 어떤지 위에 활용처 질문과 함께 알려주시면 감사하겠습니다!우선 Hibernate 과 R2dbc는 서로 약간의 대체제 관계입니다. Hibernate에서 non-blocking이 잘 지원되지 않기 때문에 R2dbc를 사용해야 하는 것이고, 지금은 Hibernate도 non-blocking에 대한 지원이 확대 되었다고는 하나 여전히 non-blocking 영역에서는 R2dbc가 더 많이 사용되는 편입니다.답변이 도움이 되었으면 좋겠습니다. 감사합니다. 🙇
- 0
- 2
- 42
질문&답변
2024.11.07
수업과는 조금 다른 실무 id취향 질문이 있습니다!
안녕하세요 영욱님! 🙂 좋은 질문 감사합니다. 질문 주신 것처럼 id를 만드는 방법에는 auto_increment를 사용하는 방식과 UUID를 사용하는 방식 그리고 snowflake처럼 bit-level에 의미를 부여해 분산 환경에서 고유한 id를 만드는 방식이 있습니다.유사 snowflake 방식은 국내에서는 거의 사용될 일 없으니 잠시 논외로 치고 id 방식과 UUID 방식을 비교해 보면, 결론부터 말씀드려 저도 id를 기본으로 사용하되, 노출이 필요한 경우에만 uuid column을 따로 만들어 관리하는 방식을 선호합니다. 그 이유는 다음과 같습니다.id 라는 개념은 관리가 무척 편리합니다. 운영상 대응도 용이한 편입니다 (여러 테이블을 조회하며 운영 대응을 해야 할 때 id를 key로 하여 소통하는 것이 기억이 어려운 uuid를 기준으로 소통하는 것보다는 살짝 나은 느낌입니다 🙂)id를 사용하는 것이 성능상 더 유리한 측면이 있습니다.UUID와 다르게 순서가 보장됩니다. (상황에 따라 마이그레이션이 더 용이합니다) 3번은 조금 더 내용을 설명 드려야 할 것 같은데요!MySQL은 secondary index를 만들 때 기본적으로 PK를 index tree 가장 뒤에 추가하게 됩니다.예를 들어 다음과 같은 테이블이 있다고 해보죠!table user - id (PK) - name - age그리고 제가 (name, age)를 인덱스로 잡는다면 실제 인덱스 트리에는 (name, age, id) 가 들어가게 됩니다! name이 32byte / age가 4byte / id가 8 byte라면 나이브 하게 생각했을 때 인덱스 node 하나의 크기는 44byte입니다.인덱스 node의 크기가 중요한 이유는 조회에 자주 사용될 인덱스 node의 크기가 작으면 작을 수록 실제 데이터를 Disk에 저장하는 단위인 Page에 더 많은 인덱스 node가 저장될 수 있기 때문입니다. 즉, 더 적은 node를 유지하면 Page를 덜 불러올 수 있고 Disk I/O를 덜 일으킬 수 있고, 조금 더 성능에 유리한 측면이 있다고 생각합니다. 만약 UUID를 - 을 제외하고 32글자를 단순 문자열로 저장하게 되면charset을 신경쓰지 않았을 때 (보통 utf8mb4를 사용하니) 128 bytecharset을 신경쎴을 때 (특정 column만 latin으로 잡으면) 32 byte인데요, charset을 매번 신경쓰기엔 살짝 쉽지 않은 측면이 있고 문자열 비교 등을 할 때에는 내부적으로 charset을 일치시켜야 하다 보니 암시적인 작업이 일어날 수도 있습니다. 그렇다고 charset을 신경쓰지 않으면 128byte라는 id에 비해 상당히 큰 크기를 갖게 되죠.그리고 물론 secondary index를 생각하지 않더라도 PK가 작으면 PK를 통해 조회가 자주 일어날 때도 도움이 되죠 🙂 H/W 성능이 무척 좋아진 요즘 저런 byte-level 최적화가 얼마나 성능에 영향을 미칠까 싶기도 하지만 똑같은 비용 (혹은 감수할만한 비용) 으로 괜찮은 효과를 볼 수 있다면 미리미리 준비해 놓는 것도 좋다고 생각합니다. 특히 이 경우에는 개인적으로 단점 대비 장점이 더 크다고 생각하고요! 당연히 id도 단점이 있을 수 있는데, 말씀해주신 것처럼 다음 데이터의 PK를 유추할 수 있다는 점입니다. 그래서 이런 경우는 uuid를 id와 별도로 따로 만들어 넣은 다음 uuid를 secondary index로 잡고 uuid를 통한 API를 만들 수 있죠.물론 이럴 때 굳이 service layer에서 uuid를 매번 만들어 넘겨주실 필요는 없고 Java를 기준으로는..public class Person { private Long id = null; // 2회 이상 사용된다면 유틸로 관리하셔도 좋습니다. private String uuid = UUID.randomUUID().toString(); }처럼 아예 기본 값을 Domain Level에서 관리해주시는 것도 방법입니다. 또한 id가 예측 가능한 것이 항상 문제인가? 라는 관점도 있을 수 있습니다. 5번 데이터 다음에 6번 데이터가 올 걸 알고 있다고 하더라도 결국 해당 데이터에 접근하기 위해서는 적절한 권한을 획득해야 하고, 그 권한이 없다면 해당 데이터에 접근하지 못할테니까요! 다음 데이터의 PK를 예측할 수는 있어도 어떻게 활용할 수가 없는거죠. (물론, 완전히 공개된 데이터는 논외이고 UUID를 사용하는 편이 좋습니다 🙂) 사실 이 내용은 약간은 어려운 내용이 많이 포함되어 있어 본 강의에서 바로 담기에 어려운 측면이 있었습니다. 언젠가 (다른 강의에서) 조금 더 정리된, 풍부한 내용으로 찾아뵐 수 있도록 하겠습니다! 🙇 좋은 질문 주셔서 감사합니다. 또 언제든지 편하게 질문 올려주세요~ 감사사합니다. 🙏
- 1
- 2
- 30
질문&답변
2024.11.05
gradle 관리 방법에대한 의견이 궁금해 문의남깁니다!
안녕하세요 영욱님!! 🙂 정말 좋은 질문 남겨주셔서 감사합니다. 이 강의를 촬영할 당시까지는 groovy 언어로 gradle 빌드 스크립트를 작성하는게 default 설정이었고, 때문에 관련 레퍼런스가 더 많아서 groovy 언어를 사용하도록 했습니다. 하지만 23년도 4월부터 kotlin이 gradle 빌드 스크립트의 default 설정이 되었고https://blog.gradle.org/kotlin-dsl-is-now-the-default-for-new-gradle-builds이 때문에 점점 build.gradle.kts 레퍼런스가 늘어나 저는 요즘 kotlin 기반의 빌드 스크립트를 사용하는 편입니다! 코틀린 언어 자체가 더 익숙하기도 하고요! ☺ (지금 준비 하고 있는 신규 강의도 kotlin 빌드 스크립트를 사용할 예정입니다!)따라서 강의는 groovy로 되어 있지만 kotlin 기반의 빌드 스크립트를 사용해보시는 것도 매우 좋다고 생각합니다! 생각보다 어렵지 않고, 요즘에는 groovy로 되어 있는 스크립트를 Chat GPT에게 넘겨 주면 대부분 kotlin을 잘 바꿔주더라고요! 또 궁금한 점 생기시면 편하게 질문 남겨주세요!감사합니다. 🙇
- 1
- 1
- 40
질문&답변
2024.11.02
확장 프로퍼티 질문
안녕하세요 영욱님~ 🙂 좋은 질문 감사드립니다. 이렇다면 확장 프로퍼티를 주로 사용하는게 isAdult같은 특정 프로퍼티를 이용해 새로운 값을 반환해주는 용도의 프로퍼티를 만드는것 위주로 사용하게 될까요??라고 표현해 주신 부분이 맞습니다. 결국 확장 프로퍼티나~ 확장 함수나~ 둘은 본질적으로 동일하기에 기존에 존재하는 프로퍼티를 이용해 새로운 값을 반환해주는 용도로 많이 활용됩니다. 다만 어쨌거나 "확장" 이기 때문에 다음과 같이 컬렉션에 대해서도 활용할 수 있는데요,예를 들어 List에서 name만 모으고 싶다면..val List.names: List get() = this.map { it.name }과 같은 식으로 코드를 작성할 수 있고 이를 제네릭과 적절히 활용하면 편리한 유틸 프로퍼티를 많이 만들어 둘 수도 있습니다. 추가로 커스텀 프로퍼티 확장 프로퍼티에서도 setter를 만들 수 있습니다! 🙂 간단한 예시로 아래 코드를 확인해보시면 좋을 것 같습니다.var Person.myName: String get() = name.plus("3") set(value) { name = value }다만 잘 아시겠지만, setter 자체를 사용할 일은 극히 드물다 보니 개인적으로 실무에서 사용해본적은 없습니다! 감사합니다. 🙇
- 1
- 1
- 41
질문&답변
2024.11.02
index.html
안녕하세요 치킨맛있어님! 🙂 질문 주셔서 감사합니다. 보다 정확히 말씀드리면 HTML 코드가 있긴 합니다!(사진)resources > static > v1 폴더 안에 index.html 을 확인하실 수 있어요. 그리고 이런 화면을 만들기 위해서는 다양한 방법을 사용하실 수 있습니다.HTML 파일을 직접 만들실 수도 있고react.js / vue.js 같은 라이브러리를 사용하실 수도 있습니다. 저는 라는 강의에서 안내해 드린 것처럼 리액트를 사용했습니다. 답변이 도움이 되었으면 좋겠습니다. 감사합니다! 🙇
- 0
- 2
- 33
질문&답변
2024.10.30
index.html 에 질문있습니다!
안녕하세요 민철님! 🙂 강의에서 말씀드린 것처럼 화면 자체는 react.js로 만들어졌습니다.이 코드는 메일 주소를 알려주시면 보내드리는데요! 이렇게 번거롭게 코드를 전달 드리는 이유는 1) 제가 FE 개발자가 아니기에 코드가 100% 최신화 되어 있지 않고 2) 실제 현업에서 생각되는 best practices와는 결이 다를 수 있기 때문입니다.메일 알려주시면 전달 드리겠습니다. 감사합니다! 🙇
- 1
- 1
- 50