Skip to content

aivle-4/backend

Folders and files

NameName
Last commit message
Last commit date

Latest commit

Β 

History

18 Commits
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 
Β 

Repository files navigation

πŸ“š AIVLE Book Service

Spring Boot 3 + JPA + WebFlux + H2 둜 κ΅¬ν˜„ν•œ κ°„λ‹¨ν•œ β€œμ±… 곡유 ν”Œλž«νΌβ€ λ°±μ—”λ“œμž…λ‹ˆλ‹€.
νšŒμ› 인증뢀터 μ±… CRUD, 그리고 OpenAI Images APIλ₯Ό μ΄μš©ν•œ μžλ™ ν‘œμ§€ 생성 κΈ°λŠ₯κΉŒμ§€ ν¬ν•¨λ˜μ–΄ μžˆμŠ΅λ‹ˆλ‹€.


πŸ“ 아킀텍쳐

Image


🌟 제곡 κΈ°λŠ₯

ꡬ뢄 μ„€λͺ… HTTP
νšŒμ› νšŒμ›κ°€μž… / 둜그인 POST /api/members/**
μ±… λͺ©λ‘ 전체·내 μ„œμž¬Β·νŽ˜μ΄μ§• GET /api/books
μ±… 검색 제λͺ©Β·μ €μž ν‚€μ›Œλ“œ GET /api/books/?keyword=
μ±… 상세 단일 쑰회 GET /api/books/{bookId}
μ±… μž‘μ„± 제λͺ©Β·λ‚΄μš©Β·ν‘œμ§€ μžλ™ 생성 POST /api/books
μ±… μˆ˜μ • 제λͺ©Β·λ‚΄μš© μˆ˜μ • PUT /api/books/{bookId}
μ±… μ‚­μ œ μ„ νƒν•œ μ±… μ‚­μ œ DELETE /api/books/{bookId}
ν‘œμ§€ 생성 OpenAI(DALL-E 2) Β· 1024Γ—1024 POST /api/books/cover

ν‘œμ§€ μ—”λ“œν¬μΈνŠΈ λ°”λ””

{
  "title": "Walking Library",
  "content": "A shy student walks into pages that become city streets…"
}

πŸ”§ 기술 μŠ€νƒ

  • μ–Έμ–΄: Java 17
  • ν”„λ ˆμž„μ›Œν¬: Spring Boot 3.x
    • Spring Web, Spring WebFlux (WebClient), Spring Data JPA, Spring Security (JWT)
  • λ°μ΄ν„°λ² μ΄μŠ€: H2
  • λΉŒλ“œ 도ꡬ: Gradle
  • μ‚¬μš© 라이브러리:
    • OpenAI Images API 연동 β†’ Spring WebFlux(WebClient), Jackson
    • JWT λ°œκΈ‰/검증 β†’ spring-security-jwt
  • 운영 ν™˜κ²½: AWS EC2

βš™οΈ ν™˜κ²½ μ„€μ •

src/main/resources/application.yml

spring:
  jackson:
    time-zone: Asia/Seoul

  datasource:
    driver-class-name: org.h2.Driver
    url: jdbc:h2:mem:testdb;
    username: sa
    password: ********

  h2:
    console:
      enabled: true
      path: /h2-console
      settings:
        web-allow-others: true

  jpa:
    hibernate:
      ddl-auto: update
    properties:
      hibernate:
        show_sql: true
        format_sql: true
        use_sql_comments: true
        default_batch_fetch_size: 1000
    open-in-view: false

jwt:
  secret: sGk+***************************

openai:
  api-key: sk-**************************
  image:
    model: dall-e-2
    size: 1024x1024

πŸ“ ν”„λ‘œμ νŠΈ ꡬ쑰

└── main
    β”œβ”€β”€ java
    β”‚   └── com.example.aivle
    β”‚       β”œβ”€β”€ AivleApplication.java                 // 메인 μ• ν”Œλ¦¬μΌ€μ΄μ…˜ 클래슀
    β”‚       β”œβ”€β”€ domain
    β”‚       β”‚   └── book
    β”‚       β”‚       β”œβ”€β”€ controller
    β”‚       β”‚       β”‚   └── BookController.java        // λ„μ„œ API μ—”λ“œν¬μΈνŠΈ
    β”‚       β”‚       β”œβ”€β”€ dto
    β”‚       β”‚       β”‚   β”œβ”€β”€ BookRequest.java            // λ„μ„œ 등둝/μˆ˜μ • μš”μ²­ DTO
    β”‚       β”‚       β”‚   β”œβ”€β”€ BookResponse.java           // λ„μ„œ 상세 쑰회 응닡 DTO
    β”‚       β”‚       β”‚   β”œβ”€β”€ BookSummaryResponse.java    // λ„μ„œ λͺ©λ‘ 쑰회 응닡 DTO
    β”‚       β”‚       β”‚   β”œβ”€β”€ CoverRequest.java           // ν‘œμ§€ 생성 μš”μ²­ DTO
    β”‚       β”‚       β”‚   └── CoverResponse.java          // ν‘œμ§€ 생성 응닡 DTO
    β”‚       β”‚       β”œβ”€β”€ entity
    β”‚       β”‚       β”‚   └── Book.java                   // λ„μ„œ μ—”ν‹°ν‹°
    β”‚       β”‚       β”œβ”€β”€ repository
    β”‚       β”‚       β”‚   └── BookRepository.java         // JpaRepository<Book, Integer>
    β”‚       β”‚       └── service
    β”‚       β”‚           β”œβ”€β”€ BookService.java            // λ„μ„œ μ„œλΉ„μŠ€ μΈν„°νŽ˜μ΄μŠ€
    β”‚       β”‚           └── BookServiceImpl.java        // λ„μ„œ μ„œλΉ„μŠ€ κ΅¬ν˜„μ²΄
    β”‚       β”œβ”€β”€ member
    β”‚       β”‚   β”œβ”€β”€ presentation
    β”‚       β”‚   β”‚   └── MemberController.java           // νšŒμ› API μ—”λ“œν¬μΈνŠΈ
    β”‚       β”‚   β”œβ”€β”€ dto
    β”‚       β”‚   β”‚   β”œβ”€β”€ LoginRequest.java               // νšŒμ› κ°€μž…/둜그인 μš”μ²­ DTO
    β”‚       β”‚   β”‚   └── LoginResponse.java              // νšŒμ› κ°€μž…/둜그인 응닡 DTO
    β”‚       β”‚   β”œβ”€β”€ entity
    β”‚       β”‚   β”‚   └── Member.java                     // νšŒμ› μ—”ν‹°ν‹°
    β”‚       β”‚   β”œβ”€β”€ repository
    β”‚       β”‚   β”‚   └── MemberRepository.java           // JpaRepository<Member, Integer>
    β”‚       β”‚   └── service
    β”‚       β”‚       β”œβ”€β”€ MemberService.java              // νšŒμ› μ„œλΉ„μŠ€ μΈν„°νŽ˜μ΄μŠ€
    β”‚       β”‚       └── MemberServiceImpl.java          // νšŒμ› μ„œλΉ„μŠ€ κ΅¬ν˜„μ²΄
    β”‚       └── global
    β”‚           β”œβ”€β”€ base
    β”‚           β”‚   └── BaseEntity.java                 // μƒμ„±μΌμžΒ·μˆ˜μ •μΌμž μžλ™ 관리
    β”‚           β”œβ”€β”€ config
    β”‚           β”‚   β”œβ”€β”€ JpaConfig.java                  // JPA μ„€μ •
    β”‚           β”‚   β”œβ”€β”€ SecurityConfig.java             // Spring Security μ„€μ • (JWT 포함)
    β”‚           β”‚   └── WebConfig.java                  // CORS λ“± μ›Ή μ„€μ •
    β”‚           β”œβ”€β”€ exception
    β”‚           β”‚   β”œβ”€β”€ CoverGenerationException.java   // ν‘œμ§€ 생성 μ‹€νŒ¨ μ˜ˆμ™Έ
    β”‚           β”‚   β”œβ”€β”€ InvalidApiKeyException.java     // 잘λͺ»λœ/λˆ„λ½λœ OpenAI API ν‚€
    β”‚           β”‚   β”œβ”€β”€ OrganizationAuthException.java  // 쑰직(Org) κΆŒν•œ λΆ€μ‘± μ˜ˆμ™Έ
    β”‚           β”‚   └── UnsupportedParameterException.java // μ§€μ›ν•˜μ§€ μ•ŠλŠ” νŒŒλΌλ―Έν„° μ˜ˆμ™Έ
    β”‚           β”œβ”€β”€ openai
    β”‚           β”‚   └── AiCoverClient.java               // OpenAI Images API 연동 ν΄λΌμ΄μ–ΈνŠΈ
    β”‚           β”œβ”€β”€ response
    β”‚           β”‚   β”œβ”€β”€ CustomException.java            // 도메인별 μ»€μŠ€ν…€ μ˜ˆμ™Έ 곡톡 λΆ€λͺ¨
    β”‚           β”‚   β”œβ”€β”€ ErrorCode.java                  // κΈ°λŠ₯별 였λ₯˜ μ½”λ“œ(enum)
    β”‚           β”‚   β”œβ”€β”€ GlobalExceptionHandler.java     // μ „μ—­ μ˜ˆμ™Έ 처리기 (@RestControllerAdvice)
    β”‚           β”‚   β”œβ”€β”€ Response.java                   // API 응닡 ν‘œμ€€ 래퍼
    β”‚           β”‚   └── SuccessCode.java                // 성곡 λ©”μ‹œμ§€ μ½”λ“œ(enum)
    β”‚           └── util
    β”‚               └── jwt
    β”‚                   β”œβ”€β”€ JwtTokenFilter.java         // JWT 인증/인가 ν•„ν„°
    β”‚                   β”œβ”€β”€ JwtTokenUtils.java          // JWT λ°œκΈ‰/검증 μœ ν‹Έ
    β”‚                   └── ResponseUtils.java          // 곡톡 응닡 생성 μœ ν‹Έ
    └── resources
        β”œβ”€β”€ static                                    
        β”œβ”€β”€ templates                                 
        β”œβ”€β”€ application.yml                           // 곡톡 μ„€μ •
        β”œβ”€β”€ application-dev.yml                       // 개발 ν™˜κ²½ μ„€μ •
        └── application-local.yml                     // 둜컬 ν™˜κ²½ μ„€μ •


πŸ–₯️ ν”„λ‘œμ νŠΈ μ„ΈλΆ€ ꡬ쑰

πŸ“Œ Domain β†’ member

νšŒμ›(Member) 도메인 κ΄€λ ¨ κΈ°λŠ₯을 λ‹΄λ‹Ήν•©λ‹ˆλ‹€.

  • μ£Όμš” κΈ°λŠ₯
    • νšŒμ› κ°€μž…(Signup), 둜그인(Login)
    • JWT 기반 인증 및 μ„Έμ…˜ 관리
domain/member
β”œβ”€ controller
β”‚   └─ MemberController.java            β–Ά νšŒμ› κ΄€λ ¨ API 제곡
β”‚       β€’ signup()         : νšŒμ› κ°€μž…
β”‚       β€’ login()          : 둜그인 (JWT λ°œκΈ‰)
β”‚       β€’ findMember()     : νšŒμ› 쑰회
β”‚
β”œβ”€ dto
β”‚   β”œβ”€ LoginRequest.java                 β–Ά νšŒμ› κ°€μž…/둜그인 μš”μ²­ DTO
β”‚   β”‚   β€’ ν•„λ“œ: loginId, password
β”‚   β”‚
β”‚   └─ LoginResponse.java                β–Ά νšŒμ› κ°€μž…/둜그인 응닡 DTO
β”‚       β€’ ν•„λ“œ: memberId
β”‚
β”œβ”€ entity
β”‚   └─ Member.java                      β–Ά νšŒμ› μ—”ν‹°ν‹°
β”‚       β€’ ν•„λ“œ: id, loginId, password  
β”‚       β€’ BaseEntity 상속(생성일/μˆ˜μ •μΌ μžλ™ 관리)
β”‚
β”œβ”€ repository
β”‚   └─ MemberRepository.java            β–Ά `JpaRepository<Member, Integer>`
β”‚       β€’ λ©”μ„œλ“œ: findByLoginId(String loginId) β†’ νšŒμ› 쑰회
β”‚
└─ service
    β”œβ”€ MemberService.java               β–Ά νšŒμ› μ„œλΉ„μŠ€ μΈν„°νŽ˜μ΄μŠ€
    β”‚   β€’ signup(LoginRequest)            : νšŒμ› κ°€μž… (쀑볡 검사 ν›„ μ €μž₯)
    β”‚   β€’ login(LoginRequest)             : 둜그인 (ID/PW 확인 β†’ JWT λ°œκΈ‰)
    β”‚   β€’ findMember(Integer memberId)     : νšŒμ› 쑰회
    β”‚
    └─ MemberServiceImpl.java           β–Ά νšŒμ› μ„œλΉ„μŠ€ κ΅¬ν˜„μ²΄
        β€’ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ‹€μ œ κ΅¬ν˜„ (쀑볡 검사, μ•”ν˜Έν™”, 토큰 생성 λ“±)

πŸ“Œ Domain β†’ book

λ„μ„œ(Book) 도메인 κ΄€λ ¨ κΈ°λŠ₯을 λ‹΄λ‹Ήν•©λ‹ˆλ‹€.

  • μ£Όμš” κΈ°λŠ₯
    • λ„μ„œ 등둝, 쑰회, μˆ˜μ •, μ‚­μ œ
    • ν‘œμ§€ 이미지(AI) 생성
domain/book
β”œβ”€ controller
β”‚   └─ BookController.java              β–Ά λ„μ„œ κ΄€λ ¨ API 제곡
β”‚       β€’ addBook()          : λ„μ„œ 등둝
β”‚       β€’ findBook() / findBooks() : λ„μ„œ 쑰회
β”‚       β€’ updateBook()       : λ„μ„œ μˆ˜μ •
β”‚       β€’ deleteBook()       : λ„μ„œ μ‚­μ œ
β”‚       β€’ generateCover()    : λ„μ„œ ν‘œμ§€(AI) 생성
β”‚
β”œβ”€ controller/dto
β”‚   β”œβ”€ BookRequest.java                 β–Ά λ„μ„œ 등둝/μˆ˜μ • μš”μ²­ DTO
β”‚   β”‚   β€’ ν•„λ“œ: title, author, content, coverImageUrl
β”‚   β”‚
β”‚   β”œβ”€ BookResponse.java                β–Ά λ„μ„œ 상세 쑰회 응닡 DTO
β”‚   β”‚   β€’ ν•„λ“œ: bookId, memberId, title, author, content, createdAt, updatedAt
β”‚   β”‚
β”‚   β”œβ”€ BookSummaryResponse.java         β–Ά λ„μ„œ λͺ©λ‘ 쑰회 응닡 DTO
β”‚   β”‚   β€’ ν•„λ“œ: bookId, title, author, createdAt, coverImageUrl
β”‚   β”‚
β”‚   β”œβ”€ CoverRequest.java                β–Ά ν‘œμ§€ 이미지 생성 μš”μ²­ DTO
β”‚   β”‚   β€’ ν•„λ“œ: title, content
β”‚   β”‚
β”‚   └─ CoverResponse.java               β–Ά ν‘œμ§€ 이미지 생성 κ²°κ³Ό DTO
β”‚       β€’ ν•„λ“œ: success, message, imageUrl
β”‚
β”œβ”€ entity
β”‚   └─ Book.java                        β–Ά λ„μ„œ μ—”ν‹°ν‹°
β”‚       β€’ ν•„λ“œ: id, title, author, content, coverImageUrl  
β”‚       β€’ 관계: μž‘μ„±μž(Member)와 @ManyToOne 연관관계
β”‚
β”œβ”€ repository
β”‚   └─ BookRepository.java              β–Ά `JpaRepository<Book, Integer>`
β”‚       β€’ ν‚€μ›Œλ“œ(제λͺ©, μ €μž) 기반 검색 λ©”μ„œλ“œ 제곡 (`findByTitleContainingIgnoreCaseOrAuthorContainingIgnoreCase`)
β”‚
└─ service
    β”œβ”€ BookService.java                 β–Ά λ„μ„œ μ„œλΉ„μŠ€ μΈν„°νŽ˜μ΄μŠ€
    β”‚   β€’ findBook(Integer bookId)           : 단건 쑰회
    β”‚   β€’ findBooks(String keyword)          : 리슀트 쑰회 (ν‚€μ›Œλ“œ 검색)
    β”‚   β€’ addBook(BookRequest, HttpSession)  : 등둝
    β”‚   β€’ updateBook(Integer, BookRequest, HttpSession) : μˆ˜μ • (본인만 κ°€λŠ₯)
    β”‚   β€’ deleteBook(Integer, HttpSession)    : μ‚­μ œ (본인만 κ°€λŠ₯)
    β”‚
    └─ BookServiceImpl.java             β–Ά λ„μ„œ μ„œλΉ„μŠ€ κ΅¬ν˜„μ²΄
        β€’ λΉ„μ¦ˆλ‹ˆμŠ€ 둜직 μ‹€μ œ κ΅¬ν˜„ (Repository 호좜, κΆŒν•œ 검증 λ“±)

πŸ“Œ Global(곡톡 μ˜μ—­)

ν”„λ‘œμ νŠΈ μ „λ°˜μ—μ„œ μ‚¬μš©λ˜λŠ” 곡톡 κΈ°λŠ₯, μ„€μ •, μ˜ˆμ™Έ 처리, OpenAI 연동 등을 λ‹΄λ‹Ήν•©λ‹ˆλ‹€.

global
β”œβ”€ base
β”‚   └─ BaseEntity.java                  β–Ά 곡톡 베이슀 μ—”ν‹°ν‹°
β”‚       β€’ @MappedSuperclass  
β”‚       β€’ ν•„λ“œ: createdAt(@CreatedDate), updatedAt(@LastModifiedDate)
β”‚       β€’ λͺ¨λ“  μ—”ν‹°ν‹°κ°€ 상속받아 생성/μˆ˜μ • 일자 μžλ™ 관리
β”‚
β”œβ”€ openai
β”‚   └─ AiCoverClient.java               β–Ά OpenAI Images API 호좜 ν΄λΌμ΄μ–ΈνŠΈ
β”‚       β€’ μ—­ν• : λ„μ„œ 제λͺ©/λ‚΄μš©μ„ λ°›μ•„ ν‘œμ§€ 생성 μš”μ²­  
β”‚       β€’ WebClient μ΄ˆκΈ°ν™” β†’ Bearer {API-KEY} 헀더 포함  
β”‚       β€’ Prompt ꡬ성 β†’ λͺ¨λΈ(dall-e-2), 크기(1024Γ—1024) JSON 전솑  
β”‚       β€’ 응닡 JSON β†’ data[0].url μΆ”μΆœ ν›„ λ°˜ν™˜  
β”‚       β€’ λ‚΄λΆ€ DTO: OpenAiImageRequest, OpenAiImageResponse (record)
β”‚       β€’ μ˜μ‘΄μ„±: Spring WebFlux(WebClient), Jackson
β”‚
β”œβ”€ response
β”‚   β”œβ”€ CustomException.java              β–Ά μ»€μŠ€ν…€ μ˜ˆμ™Έ 곡톡 λΆ€λͺ¨
β”‚   β”‚   β€’ ν•„λ“œ: ErrorCode, message  
β”‚   β”‚   β€’ 도메인별 μ˜ˆμ™Έκ°€ μƒμ†ν•˜μ—¬ μ‚¬μš©
β”‚   β”‚
β”‚   β”œβ”€ ErrorCode.java                    β–Ά κΈ°λŠ₯별 였λ₯˜ μ½”λ“œ(enum)
β”‚   β”‚   β€’ 각 μ½”λ“œ: HTTP μƒνƒœ + κΈ°λ³Έ λ©”μ‹œμ§€  
β”‚   β”‚
β”‚   β”œβ”€ GlobalExceptionHandler.java       β–Ά μ „μ—­ μ˜ˆμ™Έ 처리기 (@RestControllerAdvice)
β”‚   β”‚   β€’ CustomException, 검증 였λ₯˜(MethodArgumentNotValidException) λ“± 처리  
β”‚   β”‚
β”‚   β”œβ”€ Response.java                      β–Ά API 응닡 ν‘œμ€€ 래퍼
β”‚   β”‚   β€’ success(), error() νŒ©ν† λ¦¬ λ©”μ„œλ“œ 제곡  
β”‚   β”‚   β€’ 곡톡 응닡 JSON ꡬ쑰 톡일
β”‚   β”‚
β”‚   └─ SuccessCode.java                   β–Ά 성곡 λ©”μ‹œμ§€ μ½”λ“œ(enum)
β”‚       β€’ OK(200, "μ„±κ³΅μž…λ‹ˆλ‹€") λ“± μž¬μ‚¬μš© κ°€λŠ₯ν•œ 성곡 λ©”μ‹œμ§€
β”‚
└─ util
    └─ jwt
        β”œβ”€ JwtTokenFilter.java             β–Ά JWT 인증/인가 ν•„ν„°
        β”‚   β€’ μš”μ²­ ν—€λ”μ˜ Bearer 토큰 검증  
        β”‚   β€’ 유효 μ‹œ SecurityContext에 인증 정보 μ €μž₯
        β”‚
        β”œβ”€ JwtTokenUtils.java              β–Ά JWT μœ ν‹Έλ¦¬ν‹° 클래슀
        β”‚   β€’ AccessToken / RefreshToken 생성  
        β”‚   β€’ 토큰 검증, 만료 μ‹œκ°„ μ„€μ • λ“±
        β”‚
        └─ ResponseUtils.java              β–Ά 곡톡 응닡 생성 μœ ν‹Έ
            β€’ μ»¨νŠΈλ‘€λŸ¬μ—μ„œ κ°„λ‹¨ν•˜κ²Œ `Response.success(...)` 호좜 κ°€λŠ₯

About

No description, website, or topics provided.

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Contributors 3

  •  
  •  
  •  

Languages