🌈 [Section3] 8. [ Spring MVC ] Spring Data JPA 2

ν˜„μ£ΌΒ·2022λ…„ 11μ›” 6일
5

bootcamp

λͺ©λ‘ 보기
48/71

πŸ“• 였늘 배운 λ‚΄μš©!

  • Spring Data JPA
  • JPQL / SQL
  • Audit / Fetch / CustomBeanUtils< T >

✏️ Spring Data JPA

  • Spring Data νŒ¨λ°€λ¦¬ 기술 쀑 ν•˜λ‚˜
    ( Spring Data JDBC 도 이 쀑 ν•˜λ‚˜ )

  • JPA 기반의 데이터 μ•‘μ„ΈμŠ€ κΈ°μˆ μ„ μ’€ 더 μ‰½κ²Œ μ‚¬μš©ν•  수 있게 ν•΄μ€Œ
    ➜ 데이터 μ•‘μ„ΈμŠ€ κ³„μΈ΅μ˜ κ΅¬ν˜„μ— μžˆμ–΄ μ—¬λŸ¬λΆ„μ˜ 개발 μ‹œκ°„μ„ 단좕

πŸ’‘ JPA vs Hibernate ORM vs Spring Data JPA
β €β €

  • JPA
    ➜ Jakarta Persistence API(λ˜λŠ” Java Persistence API)
    ➜ μ—”ν„°ν”„λΌμ΄μ¦ˆ Java μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ—μ„œ κ΄€κ³„ν˜• λ°μ΄ν„°λ² μ΄μŠ€λ₯Ό μ‚¬μš©ν•˜κΈ° μœ„ν•΄ μ •ν•΄ 놓은 ν‘œμ€€ μŠ€νŽ™(Specification)
    ( ’이 κΈ°μˆ μ€ 무엇이고, 이 κΈ°μˆ μ€ μ΄λ ‡κ²Œ μ΄λ ‡κ²Œ κ΅¬ν˜„ν•΄μ„œ μ‚¬μš©ν•˜λ©΄ 돼’ 라고 적어 놓은 기술 λͺ…세라고 μƒκ°ν•˜λ©΄ 됨 )
    β €β €
  • Hibernate ORM
    ➜ JPA μŠ€νŽ™μ„ κ΅¬ν˜„ν•œ κ΅¬ν˜„μ²΄
    ➜ μ‹€μ œ μš°λ¦¬κ°€ μ‚¬μš©ν•  수 μžˆλŠ” API
    β €β €
  • Spring Data JPA
    ➜ JPA μŠ€νŽ™μ„ κ΅¬ν˜„ν•œ κ΅¬ν˜„μ²΄μ˜ API(일반적으둜 Hibernate ORM)λ₯Ό 쑰금 더 μ‰½κ²Œ μ‚¬μš©ν•  수 μžˆλ„λ‘ ν•΄μ£ΌλŠ” λͺ¨λ“ˆ
    ➜ 이λ₯Ό μ‚¬μš©ν•˜μ—¬ 데이터 μ•‘μ„ΈμŠ€ 계측 κ΅¬ν˜„

❗ 데이터 μ•‘μ„ΈμŠ€ κΈ°μˆ μ„ Spring Data JDBCμ—μ„œ Spring Data JPA둜 λ°”κΏ¨λ‹€κ³  ν•΄μ„œ μ‹€μ œλ‘œ μ½”λ“œ μžμ²΄κ°€ λŒ€ν­ λ³€κ²½λœ 뢀뢄은 μ—†μŒ
β €
➜ μ• ν”Œλ¦¬μΌ€μ΄μ…˜μ΄ νŠΉμ • κΈ°μˆ μ— κ°•ν•˜κ²Œ κ²°ν•©λ˜μ§€ μ•Šλ„λ‘ Spring이 μΆ”κ΅¬ν•˜λŠ” PSA(μΌκ΄€λœ μ„œλΉ„μŠ€ 좔상화)λ₯Ό 톡해,
κ°œλ°œμžλŠ” μΌκ΄€λœ μ½”λ“œ κ΅¬ν˜„ 방식을 μœ μ§€ν•˜λ„λ‘ ν•˜κ³ , 기술의 변경이 ν•„μš”ν•  λ•Œ μ΅œμ†Œν•œμ˜ λ³€κ²½λ§Œμ„ ν•˜λ„λ‘ μ§€μ›ν•œλ‹€

βœ” λ³΅μž‘ν•œ 검색 쑰건을 μ§€μ •ν•˜κΈ° μœ„ν•œ 방법

( ❗ Repository λ‚΄μ˜ find λ©”μ„œλ“œ μœ„μ— μ‚¬μš© )

βœ”οΈ 1. JPQL μ‚¬μš©

  • JPQLμ΄λΌλŠ” 객체 지ν–₯ 쿼리λ₯Ό 톡해 λ°μ΄ν„°λ² μ΄μŠ€ λ‚΄μ˜ ν…Œμ΄λΈ”μ„ 쑰회 κ°€λŠ₯

  • μ—”ν‹°ν‹° 클래슀의 객체λ₯Ό λŒ€μƒμœΌλ‘œ 객체λ₯Ό μ‘°νšŒν•˜λŠ” 방법
    ( ν…Œμ΄λΈ” λŒ€μƒμœΌλ‘œ 쑰회 X )

  • JPQL 문법을 μ΄μš©ν•˜μ—¬ 객체 μ‘°νšŒν•˜λ©΄, JPAκ°€ λ‚΄λΆ€μ μœΌλ‘œ JPQL을 뢄석 ν›„ μ μ ˆν•œ SQL을 λ§Œλ“€μ–΄μ„œ λ°μ΄ν„°λ² μ΄μŠ€ 쑰회, 그리고 κ·Έ μ‘°νšŒν•œ κ²°κ³Όλ₯Ό μ—”ν‹°ν‹° 객체둜 맀핑 ν›„ λ°˜ν™˜

Ex. @Query(value = "SELECT c FROM Coffee c WHERE c.coffeeId = :coffeeId")

  • coffeeId에 ν•΄λ‹Ήν•˜λŠ” 컀피 정보λ₯Ό 쑰회 ( SQL λ¬Έ μ•„λ‹˜ ! )
  • Coffee - 클래슀λͺ… / coffeeId - Coffee 클래슀의 ν•„λ“œλͺ… / c - Coffee 클래슀의 별칭
    β €
  • μœ„ 쿼리문을 μ•„λž˜μ™€ 같이도 λ³€κ²½ κ°€λŠ₯ ( SELECT cλ₯Ό μƒλž΅ν•œ ν˜•νƒœ )
    @Query(value = "FROM Coffee c WHERE c.coffeeId = :coffeeId")

[μ°Έκ³ ] https://en.wikibooks.org/wiki/Java_Persistence/JPQL
[μ°Έκ³ ] https://thorben-janssen.com/jpql/

βœ”οΈ 2. λ„€μ΄ν‹°λΈŒ SQL을 ν†΅ν•œ 쑰회

  • SQL 쿼리 문법을 μ‚¬μš©ν•˜μ—¬ λ°μ΄ν„°λ² μ΄μŠ€ λ‚΄μ˜ ν…Œμ΄λΈ” 쑰회

Ex. @Query(value = "SELECT * FROM COFFEE WHERE coffee_Id = :coffeeId")

  • nativeQuery μ• νŠΈλ¦¬λ·°νŠΈμ˜ 값을 β€˜trueβ€™λ‘œ μ„€μ •ν•˜λ©΄ value μ• νŠΈλ¦¬λ·°νŠΈμ— μž‘μ„±ν•œ SQL 쿼리가 적용

πŸ’‘ Spring Data JDBC의 @Query vs Spring Data JPA의 @Query

  • μ• λ„ˆν…Œμ΄μ…˜μ˜ 이름은 κ°™μ§€λ§Œ νŒ¨ν‚€μ§€ μžμ²΄κ°€ 닀름
    ➜ νŒ¨ν‚€μ§€ 경둜λ₯Ό ν˜Όλ™ν•˜μ§€ μ•Šλ„λ‘ μ£Όμ˜ν•΄μ•Ό 함
    β €β €
  • Spring Data JDBC의 @Query μ• λ„ˆν…Œμ΄μ…˜ νŒ¨ν‚€μ§€ 경둜
    import org.springframework.data.jdbc.repository.query.Query
    β €β €
  • Spring Data JPA의 @Query μ• λ„ˆν…Œμ΄μ…˜ νŒ¨ν‚€μ§€ 경둜
    import org.springframework.data.jpa.repository.Query

βœ”οΈ QueryDSL vs JOOQ
JPQL을 μž‘μ„±ν•  λ•Œ, λ³΅μž‘ν•œ 둜직의 경우 쿼리 λ¬Έμžμ—΄μ΄ μƒλ‹Ήνžˆ 길어짐
이 경우, 쿼리문 λ‚΄μ—˜ μ˜€νƒ€ / 문법적 였λ₯˜κ°€ μžˆλ‹€λ©΄ λŸ°νƒ€μž„ μ—λŸ¬ λ°œμƒ
➜ 이 문제λ₯Ό μ–΄λŠμ •λ„ ν•΄μ†Œν•˜λŠ”λ° κΈ°μ—¬ν•˜λŠ” ν”„λ ˆμž„μ›Œν¬λ“€μž„

[μ°Έκ³ ] https://tecoble.techcourse.co.kr/post/2021-08-08-basic-querydsl/
[μ°Έκ³ ] https://mycup.tistory.com/333


βœ”οΈ Audit κΈ°λŠ₯

λ°μ΄ν„°λ² μ΄μŠ€μ— 데이터λ₯Ό μ €μž₯ν•  λ•Œ, μƒμ„±μΌμž / μˆ˜μ •μΌμž λ“±μ˜ μ‹œκ°„μ— λŒ€ν•œ 쀑볡 μ½”λ“œλ“€μ— μžλ™μœΌλ‘œ 값을 λ„£μ–΄μ£ΌλŠ” κΈ°λŠ₯
➜ Audit κΈ°λŠ₯을 μ΄μš©ν•˜λ©΄ μžλ™μœΌλ‘œ μ‹œκ°„μ„ λ§€ν•‘ν•˜μ—¬ λ°μ΄ν„°λ² μ΄μŠ€μ˜ ν…Œμ΄λΈ”μ— λ„£μ–΄μ£Όκ²Œ 됨

  • @EnableJpaAuditing μ• λ„ˆν…Œμ΄μ…˜μ„ μΆ”κ°€

    • 보톡 @SpringBootApplication μ• λ„ˆν…Œμ΄μ…˜μ΄ μΆ”κ°€λœ ν΄λž˜μŠ€μ— λΆ™μž„

    • JPA Configuration으둜 λ³„λ„λ‘œ κ΅¬μ„±ν•˜λŠ” κ²½μš°μ— ν•΄λ‹Ή ν΄λž˜μŠ€μ— μΆ”κ°€ν•˜λŠ” 경우 많음

  • @EntityListeners (AuditingEntityListener.class)

    • μ—”ν‹°ν‹° μƒνƒœ λ³€κ²½ 감지 λ¦¬μŠ€λ„ˆ

    [μ°Έκ³ ] https://wave1994.tistory.com/161

  • @MappedSuperclass

    • 객체 μž…μž₯μ—μ„œ 곡톡 맀핑 정보가 ν•„μš”ν•  λ•Œ μ‚¬μš©

    • JPA Entity ν΄λž˜μŠ€λ“€μ΄ 이 μ• λ„ˆν…Œμ΄μ…˜μ΄ λΆ™μ–΄ κ³΅ν†΅λœ λ³€μˆ˜λ“€μ„ 정리해놓은 좔상 클래슀λ₯Ό 상속할 경우,
      ν•΄λ‹Ή 클래슀의 λ³€μˆ˜μΈ createdDate / modifiedDateλ₯Ό 컬럼으둜 인식

      ➜ 수퍼 ν΄λž˜μŠ€μ— μžˆλŠ” ν•„λ“œμ— ν¬ν•¨λ˜λŠ” 값듀도 ν…Œμ΄λΈ”μ— λ°˜μ˜ν•˜κ² λ‹€λŠ” 의미

      [μ°Έκ³ ] https://ict-nroo.tistory.com/129

      • @createdDate
        β€’ μ—”ν‹°ν‹° μƒμ„±μΌμ‹œ μžλ™ μΆ”κ°€
      • @LastModifiedDate
        β€’ μ—”ν‹°ν‹° μˆ˜μ •μΌμ‹œ μžλ™ μΆ”κ°€
      • @createdBy
        β€’ μ—”ν‹°ν‹° μƒμ„±μž μžλ™ μΆ”κ°€
        β€’ AuditorAware μΈν„°νŽ˜μ΄μŠ€λ₯Ό κ΅¬ν˜„ν•΄ μ£Όμ–΄μ•Ό 함

βœ”οΈ Fetch μ „λž΅

객체 κ·Έλž˜ν”„ 탐색을 톡해 μ—°κ΄€ 관계λ₯Ό λ§Ίκ³  μžˆλŠ” 객체λ₯Ό μ–΄λŠ μ‹œμ μ— μ‘°νšŒν•  지λ₯Ό κ²°μ •ν•˜
λŠ” μ „λž΅

  • μ¦‰μ‹œ λ‘œλ”© ( FetchType.EAGER )

    • μ—”ν‹°ν‹°λ₯Ό μ‘°νšŒν•  λ•Œ 데이터λ₯Ό μ¦‰μ‹œ κ°€μ Έμ˜΄
    • μ—°κ΄€κ΄€κ³„λ‘œ λ§€ν•‘λœ μ—”ν‹°ν‹°μ˜ λ°μ΄ν„°κΉŒμ§€ μ¦‰μ‹œ κ°€μ Έμ˜΄
    • @ManyToOne / @OneToOne
  • 지연 λ‘œλ”© ( FetchType.LAZY )

    • 데이터λ₯Ό ν•„μš”ν•œ μ‹œμ μ— κ°€μ Έμ˜΄ ( 쑰회 X / μ‹€μ œ μ‚¬μš©ν•  λ•Œ )
      ➜ μ—°κ΄€κ΄€κ³„λ‘œ λ§€ν•‘λœ μ—”ν‹°ν‹°λ₯Ό κ°€μ Έμ˜€λŠ” λ©”μ„œλ“œ(getter)λ₯Ό ν˜ΈμΆœν•˜λŠ” μ‹œμ μ— κ°€μ Έμ˜΄
    • @OnetoMany / @ManyToMany

βœ”οΈ 연관관계 맀핑 μ• λ„ˆν…Œμ΄μ…˜ 별 λ””ν΄νŠΈ Fetch μ „λž΅

  • @ManyToOne / @OneToOne
    • μ¦‰μ‹œ λ‘œλ”©μ΄ λ””ν΄νŠΈ
      ➜ μ—°κ΄€λœ μ—”ν‹°ν‹°κ°€ ν•˜λ‚˜μΌ 경우 μ¦‰μ‹œ λ‘œλ”©
  • @OnetoMany / @ManyToMany
    • 지연 λ‘œλ”©μ΄ λ””ν΄νŠΈ
      ➜ μ—°κ΄€λœ μ—”ν‹°ν‹°κ°€ μ—¬λŸ¬κ°œμΌ 경우 지연 λ‘œλ”©

[μ°Έκ³ ] https://docs.jboss.org/hibernate/orm/5.6/userguide/html_single/Hibernate_User_Guide.html#fetching
[μ°Έκ³ ] https://rutgo-letsgo.tistory.com/199

[μ°Έκ³ ] https://velog.io/@sunho6824/%EC%98%81%EC%86%8D%EC%84%B1-%EC%A0%84%EC%9D%B4%EC%99%80-N1-%EB%AC%B8%EC%A0%9C


😜 μ‹€μŠ΅

  • projects 폴더 λ‚΄μ˜ be-template-spring-data-jpa

이전 μ‹€μŠ΅μ— μΆ”κ°€λ‘œ ν•΄μ•Όλ˜λŠ” λΆ€λΆ„
1. μ—”ν‹°ν‹° 클래슀λ₯Ό Spring Data JPA에 맞게 μˆ˜μ •
2. 리포지토리(Repository) μΈν„°νŽ˜μ΄μŠ€ κ΅¬ν˜„
3. μ„œλΉ„μŠ€ 클래슀 κ΅¬ν˜„
4. 기타 κΈ°λŠ₯ μΆ”κ°€λ‘œ 인해 μˆ˜μ • 및 μΆ”κ°€λœ μ½”λ“œ

βœ”οΈ CustomBeanUtils< T >

  • μˆ˜μ •μ΄ ν•„μš”ν•œ ν•„λ“œλ§Œ λ³΅μ‚¬ν•΄μ„œ μ—…λ°μ΄νŠΈ ν•΄μ£ΌλŠ” κΈ°λŠ₯

  • Patch μ •λ³΄μ˜ 경우 μ½”λ“œκ°€ κ°„κ²°ν•΄μ§€λŠ” μž₯점이 있음

Ex. μ•„λž˜ MemberService 클래슀의 updateMember() λ©”μ„œλ“œλ₯Ό μ‚΄νŽ΄λ³΄λ©΄,

정보λ₯Ό κ°€μ Έμ™€μ„œ μˆ˜μ •ν•˜λŠ” 뢀뢄이 Optional.ofNullable 둜 길게 κ΅¬ν˜„λ˜μ–΄ 있음

➜ μ΄λŠ” μˆ˜μ •ν•΄μ•Όν•  정보듀이 λ§Žμ„ 수둝 μ½”λ“œλ„ λ§Žμ•„μ Έ μœ μ§€ 보수 λ©΄μ—μ„œ 쒋지 μ•ŠμŒ

λ”°λΌμ„œ, CustomBeanUtils< T > 클래슀λ₯Ό μ‚¬μš©ν•˜μ—¬ 이λ₯Ό κ°„λ‹¨ν•˜κ²Œ λ§Œλ“€ κ²ƒμž„

⬆️ μœ„μ™€ 같이 CustomBeanUtils< T > 클래슀λ₯Ό μ •μ˜ν•˜κ³ 

⬆️ 이λ₯Ό μ μš©ν•  MemberService ν΄λž˜μŠ€μ— μƒμ„±μžλ‘œ μ£Όμž…μ„ ν•œλ‹€ !

❗ μ—¬κΈ°μ„œ μ£Όμ˜ν•  점은 λ¨Όμ € λ³€μˆ˜λ‘œ 지정할 λ•Œ, CustomBeanUtils< T > ν΄λž˜μŠ€κ°€ μ œλ„€λ¦­μœΌλ‘œ μƒμ„±λ˜μ–΄ 있기 λ•Œλ¬Έμ— < > μ•ˆμ— μ–΄λ–€ μ—”ν‹°ν‹°μ˜ Service ν΄λž˜μŠ€μ— μ‚¬μš©μ„ 할지 μ μ–΄μ€˜μ•Όν•¨ !

⬆️ 그리고 이λ₯Ό μ μš©ν•  MemberService 클래슀 λ‚΄μ˜ updateMember() λ©”μ„œλ“œμ—

beanUtils 클래슀 λ‚΄μ˜ copyNonNullProperties λ©”μ„œλ“œ μ‚¬μš©ν•˜μ—¬ μ•ˆμ—
(μˆ˜μ •ν•˜κ³ μž ν•˜λŠ” 정보, 넣을 λ©”μ„œλ“œλͺ…)λ₯Ό λ„£κ³ 
beanUtils.copyNonNullProperties(member, findMember);

이λ₯Ό λ³€μˆ˜μ— λ„£μ–΄ κ·Έ λ³€μˆ˜λ₯Ό λ¦¬ν„΄ν•˜λ©΄ λ¦¬νŒ©ν† λ§ 끝 !

......................................................................................................................................................................................................

βœ”οΈ ResponseDto μ—μ„œ μš°μ„ λ˜λŠ” μ• λ„ˆν…Œμ΄μ…˜

  • @Builder νŒ¨ν„΄μ΄ μš°μ„ 

  • @AllArgsConstructor 이 λ‹€μŒ μš°μ„ 
    ( μƒμ„±μžμ— λͺ¨λ“  ν•„λ“œλ₯Ό νŒŒλΌλ―Έν„°λ‘œ μž…λ ₯ν•˜λŠ” 방법 )
    ( @NoArgsConsturctor λ©΄ 맀핑이 μ •μƒμ μœΌλ‘œ μ•ˆλ¨ )

  • @Setterκ°€ μš°μ„ μˆœμœ„μ˜ λ§ˆμ§€λ§‰


🌈 λŠλ‚€μ 

μ‹€μŠ΅ κ³Όμ œκ°€ 정말 μˆ˜μ •ν•  뢀뢄이 λ§Žμ•„μ„œ μ–΄λ €μ› λŠ”λ° λ„ˆλ¬΄ 쒋은 νŽ˜μ–΄λ‹˜μ„ λ§Œλ‚˜μ„œ 많이 λ°°μ›Œκ°€λŠ” μ‹œκ°„μ΄μ—ˆλ‹€ !!
직접 μˆ˜μ •ν•΄λ‚˜κ°€λ©΄μ„œ μ΄ν•΄λœ 뢀뢄도 λ§Žμ•˜λ‹€.
λ¬Όλ‘  λ‹€ μ΄ν•΄λ˜μ§„ μ•Šμ•˜μ§€λ§Œ μ°¨μ°¨ 보닀보면 결ꡭ에 λ‚˜μ€‘μ—” λ‹€ μ΄ν•΄λ˜μ–΄ μžˆκ² μ§€ !!
μ•ˆλ˜λŠ” 뢀뢄이 μžˆμ–΄μ„œ νŽ˜μ–΄κ°€ λλ‚œ 당일 저녁에도 λ§Œλ‚˜μ„œ μˆ˜μ •ν•˜κ³ , κ·Έ λ‹€μŒ 날에도 무렀 8μ‹œκ°„ λ°˜λ™μ•ˆ 같이 μˆ˜μ •ν–ˆλ‹€ !

14개의 λŒ“κΈ€

comment-user-thumbnail
2022λ…„ 12μ›” 12일

1λΉ 

3개의 λ‹΅κΈ€
comment-user-thumbnail
2022λ…„ 12μ›” 12일

ν˜„μ£Όλ‹˜ μ•ˆλ…•ν•˜μ„Έμš” μ € κΉ€ν˜œμΈμž…λ‹ˆλ‹€!! μ†”λ‘œν”„λ‘œμ νŠΈ μ—΄μ‹œλ―Έ κ²€μƒ‰ν•˜λ‹€λ³΄λ‹ˆ ν˜„μ£Όλ‹˜ λΈ”λ‘œκ·Έκ°€ λ‚˜μ™€μ„œ 잘 보고 잘 μ μš©ν•˜κ³  κ°‘λ‹ˆλ‹€ ν˜„μ£Όλ‹˜ 졜고 μ™„μ „ μ§±μ΄μ—μš”

1개의 λ‹΅κΈ€
comment-user-thumbnail
2022λ…„ 12μ›” 21일

customBeanUtils, fetch μš”λ…€μ„λ“€ 이해가 잘 μ•ˆλλŠ”λ° κΉ”λ”ν•˜κ²Œ μ •λ¦¬ν•˜μ‹  λ‚΄μš© 보고 μ΄μ œμ„œμ•Ό μ΄ν•΄ν•©λ‹ˆλ‹€ κ°μ‚¬ν•©λ‹ˆλ‹€!

1개의 λ‹΅κΈ€
comment-user-thumbnail
2023λ…„ 1μ›” 12일

ν”„λ‘œμ νŠΈ νŒ€μ›λΆ„μ΄ κ³΅μœ ν•΄μ£Όμ‹  링크인데 μ₯¬μ₯¬λ‹˜ κΈ€μ΄μ—ˆλ„€μš”!!
μ—­μ‹œ λΈ”λ‘œκ·Έ μ²œμž¬πŸ‘πŸ‘

1개의 λ‹΅κΈ€
comment-user-thumbnail
2023λ…„ 1μ›” 12일

저도 1λΉ  ν•΄μ£Όμ„Έμš”

1개의 λ‹΅κΈ€
comment-user-thumbnail
2023λ…„ 1μ›” 12일

μ•ˆλ…•ν•˜μ„Έμš” ν˜„μ£Όλ‹˜. 이전에 νŽ˜μ–΄ ν•¨κ»˜ ν–ˆλ˜ μ§„ν¬μ£Όμž…λ‹ˆλ‹€ !!
저도 νŒ€μ›λΆ„μ—κ²Œ 링크 λ°›μ•„μ„œ 보게 λλŠ”λ°, λΈ”λ‘œκ·Έ κΈ€ 정리가 λ„ˆλ¬΄ 잘 λ˜μ–΄ μžˆμ–΄μ„œ κ³΅λΆ€ν•˜λŠ”λ° 많이 도움이 λ˜λ„€μš” !!! κΈ€ κ°μ‚¬νžˆ 잘 보고 κ°‘λ‹ˆλ‹· ^>^ !!!!

1개의 λ‹΅κΈ€