본문 바로가기

카테고리 없음

24.01.31 복습2, 쿼리 dsl

로그인 성공 시, 로그인에 성공한 유저의 정보를 JWT를 활용하여 클라이언트에게 Cookie로 전달을 하려면,

 

package com.example.demo.service

import com.example.demo.dto.LoginRequest
import com.example.demo.dto.SignUpRequest
import com.example.demo.model.Member
import com.example.demo.repository.MemberRepository
import org.springframework.stereotype.Service

@Service
class MemberServiceImpl(
    private val memberRepository: MemberRepository
): MemberService {
    override fun signUp(signUpRequest: SignUpRequest): Member {
        if (signUpRequest.nickName == signUpRequest.password) {
            throw IllegalArgumentException("비밀번호는 닉네임과 같을 수 없습니다.")
        }

        if (signUpRequest.password != signUpRequest.passwordConfirm) {
            throw IllegalArgumentException("비밀번호와 비밀번호 확인이 일치하지 않습니다.")
        }

        if (memberRepository.existsByNickName(signUpRequest.nickName)) {
            throw IllegalArgumentException("이미 존재하는 닉네임입니다.")
        }

        val newMember = Member(nickName = signUpRequest.nickName, password = signUpRequest.password)
        return memberRepository.save(newMember)
    }

    override fun login(loginRequest: LoginRequest): Member {
        val member = memberRepository.findByNickName(loginRequest.nickName)
            ?: throw IllegalArgumentException("닉네임 또는 패스워드를 확인해주세요.")

        if (member.password != loginRequest.password) {
            throw IllegalArgumentException("닉네임 또는 패스워드를 확인해주세요.")
        }

        return member
    }
}

여기에서 

override fun login(loginRequest: LoginRequest): Member {
        val member = memberRepository.findByNickName(loginRequest.nickName)
            ?: throw IllegalArgumentException("닉네임 또는 패스워드를 확인해주세요.")

        if (member.password != loginRequest.password) {
            throw IllegalArgumentException("닉네임 또는 패스워드를 확인해주세요.")
        }

        return member 이녀석을         return jwtPlugin.createToken(member.nickName)

    }

이렇게 해서 jwt를 받을수 있게하고 jwtplugin을 추가한다.

그리고 

@PostMapping("/login")
fun login(@RequestBody loginRequest: LoginRequest, response: HttpServletResponse): ResponseEntity<Any> {
    return try {
        val jwt = memberService.login(loginRequest)

        val cookie = Cookie("jwt", jwt.toString())
        cookie.isHttpOnly = true
        response.addCookie(cookie)

        ResponseEntity.ok().build()
    } catch (e: IllegalArgumentException) {
        ResponseEntity.status(HttpStatus.BAD_REQUEST).body(e.message)
    }
}

이렇게 컨트롤러에 로그인 부분에 jwt를 추가한다.

@Component
class JwtPlugin() {

    companion object {
        const val SECRET = "PO4c8z41Hia5gJG3oeuFJMRYBB4Ws4aZ"
        const val ISSUER = "team.sparta.com"
        const val ACCESS_TOKEN_EXPIRATION_HOUR: Long = 240
    }

    fun validateToken(jwt: String): Result<Jws<Claims>> {
        return kotlin.runCatching {
            val key = Keys.hmacShaKeyFor(SECRET.toByteArray(StandardCharsets.UTF_8))
            Jwts.parser().verifyWith(key).build().parseSignedClaims(jwt)
        }
    }

    fun generateAccessToken(nickName: String): String {
        return generateToken(nickName, Duration.ofHours(ACCESS_TOKEN_EXPIRATION_HOUR))
    }

    private fun generateToken(nickName: String,expirationPeriod : Duration): String {
        val claims: Claims = Jwts.claims()
            .add(mapOf("nickName" to nickName))
            .build()

        val key = Keys.hmacShaKeyFor(SECRET.toByteArray(StandardCharsets.UTF_8))
        val now = Instant.now()

        return Jwts.builder()
            .issuer(ISSUER)
            .issuedAt(Date.from(now))
            .expiration(Date.from(now.plus(expirationPeriod)))
            .claims(claims)
            .signWith(key)
            .compact()
    }
}

저번에 조별과제때 햇던걸 참조해 봤는데, 내용물이 달라서 그런지 오류가 난다. 먼저, expirationPeriod얘도 타입이 일치하지 않다고 빨간줄이 떠서,

@Component
class JwtPlugin() {

    companion object {
        const val SECRET = "PO4c8z41Hia5gJG3oeuFJMRYBB4Ws4aZ"
        const val ISSUER = "team.sparta.com"
        const val ACCESS_TOKEN_EXPIRATION_HOUR: Long = 240
    }

    fun validateToken(jwt: String): Result<Jws<Claims>> {
        return kotlin.runCatching {
            val key = Keys.hmacShaKeyFor(SECRET.toByteArray(StandardCharsets.UTF_8))
            Jwts.parser().verifyWith(key).build().parseSignedClaims(jwt)
        }
    }

    fun generateAccessToken(nickName: String): String {
        val expirationPeriod: Long = Duration.ofHours(ACCESS_TOKEN_EXPIRATION_HOUR).toMillis()
        return generateToken(nickName, expirationPeriod)
    }

    private fun generateToken(nickName: String, expirationPeriod : Long): String {
        val claims: Claims = Jwts.claims()
            .add(mapOf("nickName" to nickName))
            .build()

        val key = Keys.hmacShaKeyFor(SECRET.toByteArray(StandardCharsets.UTF_8))
        val now = Instant.now()

        return Jwts.builder()
            .issuer(ISSUER)
            .issuedAt(Date.from(now))
            .expiration(Date.from(now.plusMillis(expirationPeriod)))
            .claims(claims)
            .signWith(key)
            .compact()
    }
}

 

 

이렇게  fun generateAccessToken(nickName: String): String {
        return generateToken(nickName, Duration.ofHours(ACCESS_TOKEN_EXPIRATION_HOUR))
    }이걸


fun generateAccessToken(nickName: String): String {
    val expirationPeriod: Long = Duration.ofHours(ACCESS_TOKEN_EXPIRATION_HOUR).toMillis()
    return generateToken(nickName, expirationPeriod)
} 이런식으로 바꿔준다.

그런데 ofHours가 빨간색이 떠서 알아본 결과, java.time.Duration로 해야하는데 kotlin.time.Duration이 임포트 되어 있어서 그런거였다. kotlin.time.Duration은 아직 불완전해서 작동이 잘 안된다고 한다.

 

그런제 멤버 클래스애서 Member이 빨간줄이 그어져서 알아보니,

@Entity
@Table(name = "member")
class Member(

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long,

    @Column(name = "nick_name", nullable = false, unique = true)
    var nickName: String,

    @Column(name = "password", nullable = false)
    var password: String
)

 

이렇게 @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long, 이걸 넣는걸 깜빡해서 그런거였다.

 

그리고

override fun login(loginRequest: LoginRequest): Member {
    val member = memberRepository.findByNickName(loginRequest.nickName)
        ?: throw IllegalArgumentException("닉네임 또는 패스워드를 확인해주세요.")

    if (member.password != loginRequest.password) {
        throw IllegalArgumentException("닉네임 또는 패스워드를 확인해주세요.")
    }

    return jwtPlugin.createToken(member.nickName) 여기서 createToken를 generateAccessToken
    이걸로 바꿨다.
}

 

그런데, 멤버에 id를 추가하니까

override fun signUp(signUpRequest: SignUpRequest): Member {
    if (signUpRequest.nickName == signUpRequest.password) {
        throw IllegalArgumentException("비밀번호는 닉네임과 같을 수 없습니다.")
    }

    if (signUpRequest.password != signUpRequest.passwordConfirm) {
        throw IllegalArgumentException("비밀번호와 비밀번호 확인이 일치하지 않습니다.")
    }

    if (memberRepository.existsByNickName(signUpRequest.nickName)) {
        throw IllegalArgumentException("이미 존재하는 닉네임입니다.")
    }

    val newMember = Member(nickName = signUpRequest.nickName, password = signUpRequest.password)
    return memberRepository.save(newMember)
} signUpRequest.password이부분이 빨간줄이 떠서 보니까 멤버에서 id를 삭제하라는데, 그러면 id가 작동하질 않으니
어떻게 해야하나 고민했는데,

 

@Entity
@Table(name = "member")
class Member(

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long,

@Column(name = "nick_name", nullable = false, unique = true)
var nickName: String,

@Column(name = "password", nullable = false)
var password: String
)이녀석을

@Entity
@Table(name = "member")
class Member(
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    var id: Long? = null,

    @Column(name = "nick_name", nullable = false, unique = true)
    var nickName: String,

    @Column(name = "password", nullable = false)
    var password: String
)이렇게  long? 뒤에 =null,을 넣으니 작동이 됬다.

 

 

그 아음엔 쿼리에 대한 강의를 들었다.

 

쿼리dsl은 엔티티를 따로 q클래스라는걸 따로 생성하는것이다. 기본문법은 다음과 같다.

이중 셀렉트가 가장 많이 쓰인다.

 

셀렉트 오더면 조회하고 정렬

 

 

셀렉트 프롬 포스트면 포스트에 있는 모든걸 꺼내와라. 별붙으면 모든걸 이라는 뜻

 

저렇게하면 필요한것만 꺼내와서 가독성이 좋다'

포스트에있는 타이틀에 제목이라고 들어간걸 찾아라  이게 저 웨어 부분

 

이걸 보면  포스트에 있는 모든걸 꺼내서 타이틀에 제목 들어간걸 찾아라 라는 뜻 밑의 오더는 내림차순 오름차순 정렬인데, 이건 좋아요 기준으로 정렬해라

 

그리고 join이 가장 중요.

 

이너 조인 두 테이블중 중복되는거 교집합

 

레프트 조인  a의 모든 테이블과 b도 가져오는데 a에 안맞는건 null 이렇게 하면 설명이 애매한데, 예시를 들면

 

여기서 이너조인을 하면 1,2,5가 뜸. 그게 공통점이라. 레프트 조인을 하면 

이렇게 a는 전부 나오지만 b는 1,2,5만 나오고 나머진 null

 

 

만약 미응시자 비율 알고싶다? 그럼 레프트 조인 미응시한 사람은 널 뜨니까

 

응시자중 박씨를 잦는다? 인어조인 하고 찾기

이제 1,2,5만 남고 박씨고 70점 이상만 남음 2,5가 나옴

 

// 기본 조인 (innerJoin과 동일) queryFactory.select(post) .from(post) .join(post.commet, comment) .where(comment.content.contains("아니")) .fetch()

 

// LEFT JOIN queryFactory.select(post) .from(post) .leftJoin(post.commet, comment) .where(comment.content.contains("아니")) .fetch()

둘의 차이는?

똑같다. 이유는 웨어 때문 댓글에 아니 라는게 포함되어 있는지만 가져오니까

 

// FETCH JOIN - 뒤에서 설명 예정 queryFactory.select(post) .from(post) .leftJoin(post.commet, comment) .fetchJoin() .where(comment.content.contains("아니")) .fetch()

 

페치 조인은 중요하지만 다음에 한다고 한다.