게시글
질문&답변
2023.12.04
DTO 생성 과정에서 궁금한 점이 있어요!!
이전에 이와 관련된 질문을 남겨주신 분이 계셔서 해당 글 읽어보시면 도움되실 것 같습니다.감사합니다.https://www.inflearn.com/questions/1012428/validation-%EA%B4%80%EB%A0%A8%ED%95%B4%EC%84%9C-%EC%A7%88%EB%AC%B8%EB%93%9C%EB%A6%BD%EB%8B%88%EB%8B%A4
- 0
- 3
- 403
질문&답변
2023.12.04
2.5강에서 포스트맨 오류와 전에 실습한 내용에 대해 질문 드립니다
안녕하세요 재성님. 질문 남겨주셔서 감사합니다. 제가 직접 재성님의 소스를 보지를 못해서 정확한 원인 파악은 어려우나작성하신 내용 기준으로 보면 아마도 두 강의를 하나의 프로젝트에서 작성하셔서 @RestControllerAdvice안에 있는 ExceptionHandler가 중복이 되서 문제가 일어난 것으로 추측됩니다. 두 강의가 따로 작성된 것이여서 별도의 프로젝트를 만들어서 진행하시기를 권장 드립니다.그리고 현재 하나의 프로젝트에서 사용하시려면 중복되는 ExceptionHandler를 제거해서 Exception이 발생했을때 한곳에서만 받아 처리하면 해결될 것으로 보입니다. 감사합니다.
- 0
- 2
- 423
질문&답변
2023.12.04
DTO 생성 과정에서 궁금한 점이 있어요!!
안녕하세요 평생주니어님.질문 주셔서 감사합니다.DTO 생성할때 좀 더 간편하고 쉽게 처리하는 방법을 질문 주셨는데 data class 대신에 class를 사용하면 더 간편하게 사용할 수 있습니다. data class LoginDto( @field:NotBlank @JsonProperty("loginId") private val _loginId: String?, @field:NotBlank @JsonProperty("password") private val _password: String?, ) { val loginId: String get() = _loginId!! val password: String get() = _password!! } class LoginDto( loginId: String?, password: String?, ) { @field:NotBlank val loginId: String = loginId ?: "" @field:NotBlank val password: String = password ?: "" }class를 사용해서 null 허용 타입으로 일단 DTO에 데이터를 담고 중괄호 안에 프로퍼티를 두고 validation 어노테이션을 사용해서 체크를 하면서 null 불가 타입은 DTO를 사용시 null 고려없이 사용하는 방법도 있습니다. 감사합니다.
- 0
- 3
- 403
질문&답변
2023.11.07
1 : N 필드 `memberRole` 에 @OneToMany 옵션 cascade 미사용, 컬랙션 null 초기화 에 대한 질문입니다.
안녕하세요. Truestar님 질문 남겨주셔서 감사합니다.두가지 질문에 답변을 같이 남기자면 해당 강의가 초보자를 대상으로 Spring Security와 JWT를 한번 사용해보는 강의여서 예제가 그렇게 되었습니다.Q1. cascade 미사용=> cascade 를 사용하면서 설명을 하려면 영속성 컨텍스트를 같이 설명을 해야해서 해당 부분은 제외하고 서비스에서 바로 넣어주는 코드를 작성하였습니다. 다른 이유는 없고 이렇게 하는게 초보자분들이 이해하시는데 쉬울 것으로 판단했습니다. Q2. 컬렉션 null 초기화=> 이것도 내용은 동일합니다.@OneToMany(fetch = FetchType.LAZY, mappedBy = "member") val memberRole: List? = null해당 예제에서 memberRole을 권한 확인시 조회용으로만 쓰려고 했습니다.회원가입시 DB에 바로 저장만 하고 끝나기에 memberRoleRepository를 사용해서 memberRole을 저장하면서 member 엔티티에 연결해주지 않았습니다.그래서 빈 리스트로 미리 초기화할 필요가 없어서 null로 넣었습니다. 실제 개발을 할때는 여러가지를 고려해서 개발을 진행하지만 해당 강의는 초보자가 쉽게 따라할 수 있는 강의를 만드는데 포커싱을 두고있어서 보실때 이상한 예제가 있을 수 있을 것 같습니다. 다음번에는 좀 더 확실하고 쉬운 예제로 찾아뵙도록 하겠습니다. 감사합니다 :)
- 0
- 2
- 321
질문&답변
2023.11.07
build.gradle.kts 오류가 발생해요
안녕하세요 재성님 질문 남겨주셔서 감사합니다. 다시 빌드해서 해결되지 않았다면 캐시 문제가 아닐까 생각됩니다.File -> Invalidate Caches... -> Invalidate and Restart 눌러서 IntelliJ 다시 시작해보시면 해결 될 수도 있습니다. (사진)(사진)한번 해보시고 안되시면 다시 질문 부탁드립니다. 감사합니다 :)
- 0
- 2
- 2K
질문&답변
2023.09.20
loginId는 왜 변경이 안될까요?
안녕하세요 jjang9님 질문주셔서 감사합니다. 회원정보 수정하는 부분 질문 주셨는데요.이거는 JPA 설정중에 update시에는 loginId는 수정이 되지 않게 해놓았기 때문입니다.class Member( @Id @GeneratedValue(strategy = GenerationType.AUTO) var id: Long? = null, @Column(nullable = false, length = 30, updatable = false) val loginId: String, @Column(nullable = false, length = 100) val password: String, @Column(nullable = false, length = 10) val name: String, @Column(nullable = false) @Temporal(TemporalType.DATE) val birthDate: LocalDate, @Column(nullable = false, length = 5) @Enumerated(EnumType.STRING) val gender: Gender, @Column(nullable = false, length = 30) val email: String, )Member entity보시면 loginId 위에 설정을 먹여뒀는데 updatable = false 라고 해두면 해당 컬럼은 insert때만 넣고 update 할때는 제외하게 됩니다.@Column(nullable = false, length = 30, updatable = false)그래서 회원정보 수정을 할때 loginId가 변하지 않게 됩니다.해당 설정 풀어보시고 회원정보 수정해보시면 loginId가 변경 될겁니다. 더 궁금하신 점 있으시면 질문 남겨주세요~감사합니다 :)
- 0
- 1
- 273
질문&답변
2023.09.14
Validation 관련해서 질문드립니다.
JUNN님 추가로 질문주셔서 감사합니다.JUNN님 질문을 읽고 다시 한번 DTO를 어떤식으로 활용하는게 좋을지 생각해봤습니다.RequestDto의 경우 모든 값이 다 담겨져 있어야 한다면 왜 꼭 null 또는 "" 을 허용해줘야 하는가? 이걸 허용 안 해준다면 String? 이 아닌 String을 사용하면 안 되는 것일까?=> 이 부분은 프론트쪽과 값을 주고 받는 부분이 맞아떨어진다면 String으로 선언하셔도 됩니다. 애초에 프론트에서 null이나 ""를 넘기지 않을테니까요. 그런데 String?으로 했던 이유를 조금 말씀드리자면 저는 클라이언트(프론트화면일 수도 있고 postman일수도 있는)에서 오는 Request는 언제든지 틀릴 수 있다는 전제가 있습니다. 그래서 그것을 검증하는 일환으로 Validation을 사용합니다. 예를 들어보겠습니다. Request body에 아래의 값을 전달한다고 가정해보겠습니다.(loginId, password 필수값){ "loginId":null, "password":null } String 으로 선언class LoginDto( @field:NotBlank val loginId: String, @field:NotBlank val password: String, )String으로 선언하고 값을 전달하면 request에 아래와 같이 나오게 됩니다.{ "resultCode": "ERROR", "data": { "미처리 에러": "JSON parse error: Instantiation of [simple type, class com.example.auth.member.dto.LoginDto] value failed for JSON property loginId due to missing (therefore NULL) value for creator parameter loginId which is a non-nullable type" }, "message": "에러가 발생했습니다." }그러면 loginId에 null을 넣을 수 없다고 error메세지를 출력하게 됩니다. String? 으로 선언class LoginDto( @field:NotBlank val loginId: String?, @field:NotBlank val password: String?, )String?으로 선언하고 값을 전달하면 request에 아래와 같이 나오게 됩니다.{ "resultCode": "ERROR", "data": { "loginId": "공백일 수 없습니다", "password": "공백일 수 없습니다" }, "message": "에러가 발생했습니다." }그러면 loginId, password 둘 다 안되는 이유를 Response에 담아서 보내줄 수 있습니다. 이것으로 보면 String으로 선언하면 Validation 체크를 하기 전에 DTO에 담는 과정에서 error를 발생시키는 것을 알 수 있는데요. 예를 들어 항목이 10개인데 그 항목들 모두 문제가 있다면 상황에 따라 문제가 있는 1개의 항목만 Request 보낸 쪽에서 알 수 있습니다. String?로 선언해서 DTO에 데이터를 담는 과정에서 문제를 발생시키지 않는다면 Validation으로 모든 항목을 검사하고 모든 문제를 Request 보낸 쪽에 알려 줄 수 있습니다.{ "resultCode": "ERROR", "data": { "loginId": "공백일 수 없습니다", "name": "공백일 수 없습니다", "birthDate": "날짜형식(YYYY-MM-DD)을 확인해주세요", "password": "영문, 숫자, 특수문자를 포함한 8~20자리로 입력해주세요", "gender": "MAN 이나 WOMAN 중 하나를 선택해주세요", "email": "올바른 형식의 이메일 주소여야 합니다" }, "message": "에러가 발생했습니다." }그래서 회원가입쪽을 보시면 Validation으로 처리했을때의 장점을 보실 수 있습니다. 1번과 같은 접근이라면 UpdateDto의 경우에 만약 모든 값을 수정하는 것이 아니라 일부 프로퍼티에만 값이 담겨져서 오면 이럴 때는 String? 타입을 사용해야 되는 것일까?=> 저의 서비스 운영 관점에서 본다면 update를 해야하는 경우 일부 값이 변경이 되어도 항목들 전체를 받아서 전체를 update치는게 좋다고 생각합니다. 일부를 update하게 되면 더 효율적으로 보일 수 있겠으나 각 항목의 변경시점과 변경자가 달라져 문제가 발생했을때 원인을 찾아가는 것에 추가적인 리소스가 발생한다고 생각합니다.일부 프로퍼티만 값을 업데이트 치는 것으로 예를 들어 보겠습니다.name : "홍길동", age : 30 라는 정보를 A유저는 name = "임꺽정"B유저는 age = 40으로 업데이트치는 Request를 같은 시점에 보냈다고 하면 A의 입장에서는 name만 변경했는데 age까지 변경이 된 것으로 보일겁니다. B도 마찬가지 입장이겠죠. 상황에 따라 다르지만 2가지 update 가 넘어올때 최종 변경 데이터를 가지고 있도록 만드는 경우가 많습니다.그래서 전체 항목을 받아서 현재 데이터에 업데이트를 치는 것이죠A유저의 업데이트가 먼저 실행됐다면 B유저의 name = "홍길동", age = 40으로 덮어씌워 최종적으론 B유저의 정보만 남게 됩니다.예시가 적절하지 않았을 수 있으나 이런 관점에서 본다면 전체 프로퍼티를 받게 되는 것이고 각 프로퍼티의 성격에 따라서 타입과 Validation을 지정하는 것이 좋은 방법이라고 생각됩니다. 질문에 대한 적절한 답이 되셨으면 좋겠습니다. 저도 JUNN님 덕분에 해왔던대로 그냥 개발을 하지 않았는지 이 부분은 왜 이렇게 사용했었는지 다시 한번 생각하게 됐습니다. 감사합니다. 앞으로 또 궁금하신점 생기시면 언제든지 질문 부탁드립니다.감사합니다 :)
- 1
- 2
- 555
질문&답변
2023.09.13
memberRole필드 질문이요~
안녕하세요. juuu o님 질문주셔서 감사합니다.@Entity @Table( uniqueConstraints = [UniqueConstraint(name = "uk_member_login_id", columnNames = ["loginId"])] ) class Member( @Id @GeneratedValue(strategy = GenerationType.AUTO) var id: Long? = null, @Column(nullable = false, length = 30, updatable = false) val loginId: String, @Column(nullable = false, length = 100) val password: String, @Column(nullable = false, length = 10) val name: String, @Column(nullable = false) @Temporal(TemporalType.DATE) val birthDate: LocalDate, @Column(nullable = false, length = 5) @Enumerated(EnumType.STRING) val gender: Gender, @Column(nullable = false, length = 30) val email: String, ) { @OneToMany(fetch = FetchType.LAZY, mappedBy = "member") val memberRole: List? = null private fun LocalDate.formatDate(): String = this.format(DateTimeFormatter.ofPattern("yyyyMMdd")) fun toDto(): MemberDtoResponse = MemberDtoResponse(id!!, loginId, name, birthDate.formatDate(), gender.desc, email) }이 엔티티에서 질문주신 부분은 MemberRole과 조인을 위한 프로퍼티인데요.@OneToMany(fetch = FetchType.LAZY, mappedBy = "member") val memberRole: List? = null아래의 클래스를 풀어서 적어보면 constructor가 생략이 되어있는 형태입니다.class Member( ... ) -> class Member constructor( ... )그래서 중괄호( ... ) 안에는 생성자에 사용될 프로퍼티만 넣어주면 되기에 memberRole은 거기에 있을 필요가 없어서 본문에 선언 했습니다. 다른 예시를 들어보자면class Person constructor( name: String, age: Int, ) { val name: String = name val age: Int = age val gender: String? = null }위 소스는 생성자로 name과 age라는 변수를 받아서 본문에 있는 name 프로퍼티와 age 프로퍼티에 넣어주는 소스인데요.이 소스를 프로퍼티와 생성자를 동시에 사용하는걸로 작성하면 아래의 코드가 됩니다.class Person( val name: String, val age: Int, ) { val gender: String? = null }그래서 역시 중괄호( ... ) 안에는 생성자에 필요한 프로퍼티만 들어간 것을 확인하실 수 있습니다. 질문에 대한 답변이 되셨으면 좋겠습니다. 제가 질문의 의도를 잘못 이해했을 수 있으니 보시고 궁금하신점 있으시면 추가 질문 부탁드립니다. 강의 봐주시고 질문까지 남겨주셔서 다시 한번 감사합니다 :)
- 0
- 1
- 376
질문&답변
2023.09.13
Validation 관련해서 질문드립니다.
안녕하세요 JUNN님 칭찬과 함께 질문까지 주셔서 감사합니다.저도 강의 자료 만들면서 DTO에서 Validation 처리를 어떻게 하는 것이 합리적일지 고민을 했는데요. 그래서 그런지 질문이 더욱 와닿았습니다. 강의시 만들었던 LoginDto 기준으로 다른 방안을 말씀 드려보겠습니다.data class LoginDto( @field:NotBlank @JsonProperty("loginId") private val _loginId: String?, @field:NotBlank @JsonProperty("password") private val _password: String?, ) { val loginId: String get() = _loginId!! val password: String get() = _password!! }제가 DTO에 이런식으로 만들었던 몇가지 이유가 있었습니다.DTO에서 Validation처리를 한다.Validation 체크시 빈값이 아닌게 확인되면 Service에서 해당 DTO를 쓸때 null이 아님을 보장한다.Request의 body에 아래와 같이 넘어오는 모든 데이터는 빈값으로 여긴다.해당 key의 값이 null로 넘어오는 경우{ "loginId":null, "password":null }해당 key의 값이 ""로 넘어오는 경우{ "loginId":"", "password":" " }해당 key가 안넘어오는 경우{} 그래서 처리 순서를 아래와 같이 잡았습니다.DTO에 데이터 담기(값이 어떻게 넘어올지 모르니 null을 허용)Validation 체크뽑아쓸때 null 불가 custom getter 그래서 이번에 data class가 아닌 class를 사용해서 DTO를 만들었습니다.변경한 코드는 아래와 같습니다.class LoginDto( loginId: String?, // 1 password: String?, ) { @field:NotBlank // 3 val loginId: String = loginId ?: "" // 2 @field:NotBlank val password: String = password ?: "" }생성자에서 null 허용타입으로 값을 받아오기그것을 프로퍼티에 넣어줄때 elvis operator( ?: ) 사용해서 default 값을 ""으로 넣어주기Validation 체크 이렇게 하면 해당 DTO의 값을 뽑아쓸때 null 불가 타입이여서 null이 아님을 보장하게 됩니다.이 방법은 data class를 사용하기는 어렵습니다. data class는 기본적으로 생성자에 선언된 프로퍼티 기준으로 equals, hashCode, toString 등의 함수를 생성하는데요. 지금 제시한 방법은 본문에 프로퍼티를 선언하면서 처리를 하게 한 것이여서 data class를 사용해서 하는 방식으로는 어려울듯 합니다. 해결 방법은 아니고 이것저것 해보면서 나온 과정 중에 아직 풀지 못한 부분이지만 공유 드립니다. 제가 처음에 생성자에서 바로 프로퍼티를 선언하면서 처리할 수 있는 방법이 없을지 이것저것 해봤었는데요.class LoginDto( @field:NotBlank val loginId: String, @field:NotBlank val password: String, )위 코드를 JAVA로 바꿔서 생성자 부분을 보면 아래와 같습니다.public LoginDto(@NotNull String loginId, @NotNull String password) { Intrinsics.checkNotNullParameter(loginId, "loginId"); Intrinsics.checkNotNullParameter(password, "password"); super(); this.loginId = loginId; this.password = password; }여기서 필드에 데이터를 담아야 Validation 체크를 할텐데 그 전에 null 체크가 실행되면서 Validation 체크 전에 error를 떨어뜨립니다.Service에서 해당 DTO의 값을 불러올때 null 허용되지 않는 타입으로 가져오기위해 String을 쓰니 생성자에서 이미 체크를 해버리고 error를 발생시키더라구요.그래서 이렇게 하니 애초에 DTO에 데이터를 담는 과정에서 문제가 발생했습니다. 그래서 말씀드린 다른 방법처럼 전달받는 값은 null 허용으로 받으면서 프로퍼티에 넣어줄때 null인 경우 default 값으로 ""을 넣었습니다.그렇게 하면 JAVA로 변경한 생성자가 아래와 같이 만들어집니다.public LoginDto(@Nullable String loginId, @Nullable String password) { String var10001 = loginId; if (loginId == null) { var10001 = ""; } this.loginId = var10001; var10001 = password; if (password == null) { var10001 = ""; } this.password = var10001; } 제 답변이 도움이 되셨으면 좋겠습니다. 이후 더 괜찮은 방법 찾게되면 공유 드리겠습니다.강의 봐주시고 이렇게 질문까지 주셔서 다시 한번 감사드립니다 :)
- 1
- 2
- 555
질문&답변
2023.08.29
findTop10ByOrderByCntDesc 함수의 동작 원리에 대한 질문
안녕하세요. 김정훈님 질문 남겨주셔서 감사합니다. 질문하신 함수(메서드)들을 Query Methods 라고 합니다. 이것은 Spring Data JPA에 포함되어 있는 기능인데요. Repository interface에 메소드 명으로 query를 자동으로 생성해줍니다.fun findTop10ByOrderByCntDesc() :List이렇게 되어있는 메소드 명은 아래의 SQL 만들어서 호출 시 사용하게 됩니다.select w1_0.word, w1_0.cnt from wordcount w1_0 order by w1_0.cnt desc limit ? Query Methods도 JPA에서 좀 더 쉽게 Query를 사용하기 위한 방법이여서 작성 규칙이 있습니다.아래의 레퍼런스 확인하시면 작성법 학습 하실 수 있습니다.https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods 보시고 더 궁금하신 내용 있으시면 질문 남겨주세요 :)
- 0
- 1
- 213