Skip to content
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
3365700
[chore] project setup
devops3199 Jan 7, 2025
05e045c
[chore] update readme
devops3199 Jan 8, 2025
cf57e7e
[refactor] change module name. api -> movie-api
devops3199 Jan 8, 2025
73f4657
[chore] change architecture image
devops3199 Jan 8, 2025
182bb6c
[chore] change erd image
devops3199 Jan 8, 2025
ec6816c
[chore] change erd image 2
devops3199 Jan 8, 2025
91ebafe
[fix] join table
devops3199 Jan 9, 2025
4bb0c93
[chore] remove unused import
devops3199 Jan 9, 2025
ee32bb4
[feat] docker-compose database
devops3199 Jan 9, 2025
31dbfd5
[chore] change directory for readme imgs
devops3199 Jan 9, 2025
11f31b3
[chore] update readme
devops3199 Jan 9, 2025
aec1f8b
[fix] movie status mock data
devops3199 Jan 9, 2025
56ae71b
[feat] sorting movies
devops3199 Jan 9, 2025
a5e8c91
[fix] change to LessThanEqual
devops3199 Jan 9, 2025
114abc3
[feat] add error advice controller
devops3199 Jan 9, 2025
64cc0b8
[chore] update readme
devops3199 Jan 9, 2025
c1bbe56
[fix] add movie status condition when fetch data
devops3199 Jan 9, 2025
3db3353
[refactor] search dto
devops3199 Jan 10, 2025
5633132
[refactor] create root build.gradle & add common used library
devops3199 Jan 10, 2025
d2313e7
[chore] add more mocked data
devops3199 Jan 10, 2025
c817a13
[fix] apply pr review
devops3199 Jan 11, 2025
e89180b
[fix] change to @Repository
devops3199 Jan 11, 2025
f610fbd
[refactor] advance clean architecture & consists of 4 modules 2
devops3199 Jan 13, 2025
607b68a
[refactor] change module name. adapter -> infrastructure
devops3199 Jan 14, 2025
2cfc25f
[chore] update readme
devops3199 Jan 14, 2025
7cfd130
[feat] add TheaterSeat enum
devops3199 Jan 14, 2025
4e0495d
[refactor] port names 2
devops3199 Jan 14, 2025
a39f67d
[fix] db_movie_showtime table start,end column type
devops3199 Jan 15, 2025
92a415d
[feat] add redis docker
devops3199 Jan 15, 2025
2e37685
[feat] add enum validator annotation
devops3199 Jan 15, 2025
83c2d2d
[feat] apply Cacheable feature
devops3199 Jan 15, 2025
7486542
[refactor] dependencies between modules
devops3199 Jan 17, 2025
ae8fa2c
[chore] undo application.yml changes
devops3199 Jan 17, 2025
4c82c1c
[refactor] querydsl dependency
devops3199 Jan 18, 2025
ec4c4e6
[chore] refactor non-code files
devops3199 Jan 18, 2025
3d5dc75
[fix] npe error from request dto
devops3199 Jan 18, 2025
2aa1649
[test] add http file
devops3199 Jan 18, 2025
74f6bcf
[test] add k6 script
devops3199 Jan 18, 2025
5c796ae
[feat] add redisson
devops3199 Jan 19, 2025
00d8bf4
[chore] add http
devops3199 Jan 19, 2025
b700933
[chore] update readme
devops3199 Jan 19, 2025
2f7743a
[refactor] movie-api request dto convert logic
devops3199 Jan 19, 2025
1915964
[refactor] apply code review
devops3199 Jan 21, 2025
6f8b81e
[chore] add index to ddl script
devops3199 Jan 21, 2025
0a8fea6
[test] add 5 stages. stress test!
devops3199 Jan 21, 2025
ab3bcbb
[chore] add endpoint to .http file
devops3199 Jan 21, 2025
1b11e97
[fix] complex cache key
devops3199 Jan 21, 2025
fc78548
[test] apply sample test code
devops3199 Jan 22, 2025
5e94eb3
[feat] 예약 API 구현
devops3199 Jan 25, 2025
f1548af
[test] pessimistic lock 구현 & 테스트코드 환경 세팅 & 테스트코드 작성
devops3199 Jan 26, 2025
ebade15
[test] optimistic lock 구현
devops3199 Jan 26, 2025
9edcaab
[feat] send message 구현
devops3199 Jan 26, 2025
d994a71
[test] AOP 기반 distributed lock 구현
devops3199 Jan 26, 2025
4780336
[test] 함수형 기반 distributed lock 구현
devops3199 Jan 26, 2025
12de380
[test] 분산락 성능 테스트 및 README 업데이트
devops3199 Jan 26, 2025
7a62b2c
[fix] 낙관적락 & 분산락 함께 적용
devops3199 Jan 27, 2025
eb840f9
[fix] PR 리뷰 적용
devops3199 Jan 27, 2025
14ce595
[feat] 단일 서버 RateLimit 구현
devops3199 Jan 29, 2025
74f84fa
[feat] 분산 환경 RateLimit 구현
devops3199 Jan 30, 2025
620e443
[fix] 예약 성공 시 rate limit 적용
devops3199 Feb 1, 2025
44841c1
[test] 테스트 코드 작성
devops3199 Feb 1, 2025
0103907
[test] @DataJpaTest로 변경
devops3199 Feb 2, 2025
2fd597c
[chore] update readme
devops3199 Feb 2, 2025
ed9e3f8
[fix] SeatRepositoryTest 테스트 조건 수정
devops3199 Feb 2, 2025
3aba743
[refactor] key generator 유틸 생성
devops3199 Feb 2, 2025
e5c4f91
[refactor] PR 리뷰 적용
devops3199 Feb 2, 2025
f6068e8
[refactor] 테스트 코드 메소드 이름 및 변수명
devops3199 Feb 4, 2025
bf10709
[refactor] use-case 로직 -> domain 로직으로 이동
devops3199 Feb 4, 2025
47f4f76
[test] domain 테스트 작성
devops3199 Feb 4, 2025
11a7b32
[chore] readme 업데이트
devops3199 May 17, 2025
4aec07b
[chore] readme 업데이트
devops3199 May 17, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Spring Boot
*.log
*.class

# Gradle
.gradle/

# IntelliJ IDEA
.idea/
54 changes: 51 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,51 @@
## [본 과정] 이커머스 핵심 프로세스 구현
[단기 스킬업 Redis 교육 과정](https://hh-skillup.oopy.io/) 을 통해 상품 조회 및 주문 과정을 구현하며 현업에서 발생하는 문제를 Redis의 핵심 기술을 통해 해결합니다.
> Indexing, Caching을 통한 성능 개선 / 단계별 락 구현을 통한 동시성 이슈 해결 (낙관적/비관적 락, 분산락 등)
# [본 과정] 이커머스 핵심 프로세스 구현

## How to use

```bash
docker compose up -d
```
```bash
curl -X GET http://localhost:8080/api/movies?showDate=2022-05-18
curl -X GET http://localhost:8080/api/movies?showDate=2022-05-17
```

## Multi Module
총 3개 모듈로 구성되있습니다.

### 1. movie-api
> 영화 도메인을 담당하고 Presentation Layer를 담당하는 모듈입니다.
- GET /api/movies?showDate=2025-01-01

```json
// 응답 예시
[
{
"id": 0,
"title": "나 홀로 집에",
"description": "...",
"rating": "전체관람가",
"genre": "코미디",
"thumbnail": "https://...",
"runningTime": 103,
"releaseDate": "1991-07-06",
"theaters": ["강남점", "안양점"],
"showtimes": ["08:00 ~ 09:45", "10:00 ~ 11:45"]
}
]
```
### 2. core
> 공통으로 사용하는 Entity, DTO를 담당하는 모듈입니다.
### 3. infrastructure
> DB 인터페이스를 담당하는 모듈입니다.
> MySQL, Redis 연결을 담당하고 데이터 입출력 로직을 포함합니다.
> DB가 변경되어도 api, core 모듈의 코드는 최소로 변경합니다.

## Architecture
> 다른 도메인 확장성을 고려한 설계입니다.
> 모든 api 모듈에서는 Entity -> DTO로 변환하여 리턴합니다.

![arc](readme/arc.png)

## Table Design
![erd_db](readme/erd.png)
36 changes: 36 additions & 0 deletions core/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
../.gradle
build/
!gradle/wrapper/gradle-wrapper.jar
!**/src/main/**/build/
!**/src/test/**/build/

### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
bin/
!**/src/main/**/bin/
!**/src/test/**/bin/

### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
out/
!**/src/main/**/out/
!**/src/test/**/out/

### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/

### VS Code ###
.vscode/
34 changes: 34 additions & 0 deletions core/build.gradle

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 core 모듈과 다른 모듈에 공통적인 dependency (e.g jpa)가 있습니다.

  • 아키텍처 의도로 보면 공통적인 부분은 코어모듈에 의존성을 가지도록 설계하신것 같은데 중복되는 의존성은 제거해도 좋을 것 같습니다 :)
  • 루트 프로젝트의 build.gradle에서 all-project, sub-project의 종속성들을 한 번에 관리해주는 방법도 있습니다.
    • 이 방법은 프로젝트를 진행하는 팀들의 컨벤션에따라 달라질 수도 있겠네요 :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아래처럼 수정했습니다! allprojects, subprojects 차이점이 뭘까요? 둘 다 모든 프로젝트에 영향을 주는 설정 같아서요.

root / build.gradle

plugins {
    id 'java'
    id 'org.springframework.boot' version '3.3.2'
    id 'io.spring.dependency-management' version '1.1.6'
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

allprojects {
    group = 'com.example'
    version = '0.0.1-SNAPSHOT'

    repositories {
        mavenCentral()
    }
}

subprojects {
    apply plugin: 'java'
    apply plugin: 'org.springframework.boot'
    apply plugin: 'io.spring.dependency-management'

    dependencies {
        implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
        implementation 'org.springframework.boot:spring-boot-starter-validation'
        implementation 'org.springframework.boot:spring-boot-starter-web'

        compileOnly 'org.projectlombok:lombok'
        annotationProcessor 'org.projectlombok:lombok'

        developmentOnly 'org.springframework.boot:spring-boot-devtools'

        testImplementation 'org.springframework.boot:spring-boot-starter-test'
        testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
    }
}

movie-api / build.gradle

dependencies {
    implementation project(':core')
    implementation project(':infrastructure')
}

bootJar {
    enabled = true
}

tasks.named('test') {
    useJUnitPlatform()
}

core / build.gradle

dependencies {
    implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.2'
}

bootJar {
    enabled = false
}

tasks.named('test') {
    useJUnitPlatform()
}

infrastructure / build.gradle

dependencies {
    implementation project(':core')
    runtimeOnly 'com.mysql:mysql-connector-j'
}

bootJar {
    enabled = false
}

tasks.named('test') {
    useJUnitPlatform()
}

Copy link

@soonhankwon soonhankwon Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • allprojects는 루트 프로젝트와 모든 하위 프로젝트에 공통 설정 적용
  • subprojects는 모든 하위 프로젝트들에만 적용할 설정
    루트에서 application을 구동시키는 경우도 있고, 다른 상황도 있을 수 있어 이 경우에 allprojects도 사용됩니다.

현재 찬엽님 프로젝트에서는 root 모듈의 subprojects들에 모든 모듈이 포함됨으로 subprojects만 사용해도 관리 가능합니다 :)

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
plugins {
id 'java'
id 'org.springframework.boot' version '3.3.2'
id 'io.spring.dependency-management' version '1.1.6'
}

group = 'com.example'
version = '0.0.1-SNAPSHOT'

java {
toolchain {
languageVersion = JavaLanguageVersion.of(21)
}
}

repositories {
mavenCentral()
}

dependencies {
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-validation'
implementation 'com.fasterxml.jackson.core:jackson-annotations:2.17.2'

compileOnly 'org.projectlombok:lombok'
annotationProcessor 'org.projectlombok:lombok'

testImplementation 'org.springframework.boot:spring-boot-starter-test'
testRuntimeOnly 'org.junit.platform:junit-platform-launcher'
}

tasks.named('test') {
useJUnitPlatform()
}
25 changes: 25 additions & 0 deletions core/src/main/java/com/example/app/common/BaseEntity.java

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

System Properties를 BaseEntity로 정의해서 사용을 잘하셨습니다!

  • 다만, Data 애노테이션의 모든 기능이 필요한 건지? 최대한 필요한 메서드만 캡슐화의 관점에서 사용하시면 더 좋을듯 합니다.
  • BaseEntity 클래스는 직접 생성되지 않고 상속을 통해서만 사용되기 때문에, abstract로 선언하면 더 설계 의도가 명확해질 것 같습니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Getter 어노테이션으로 변경했고 의견주신대로 abstract으로 선언하였습니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.example.app.common;

import jakarta.persistence.Column;
import jakarta.persistence.EntityListeners;
import jakarta.persistence.MappedSuperclass;
import lombok.Data;
import org.springframework.data.annotation.CreatedDate;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.jpa.domain.support.AuditingEntityListener;

import java.time.LocalDateTime;

@Data
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class)
public class BaseEntity {

@LastModifiedDate
@Column(insertable = false)
private LocalDateTime updatedAt;

@CreatedDate
@Column(updatable = false)
private LocalDateTime createdAt;
}
4 changes: 4 additions & 0 deletions core/src/main/java/com/example/app/common/ErrorMessage.java

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  • 불변성을 보장하는 record 사용 적절히 잘 하셨습니다 👍

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.example.app.common;

public record ErrorMessage(String errorCode, String errorMessage) {
}
55 changes: 55 additions & 0 deletions core/src/main/java/com/example/app/movie/dto/MovieDto.java

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 MovieDto가 Api 스펙(http response)라고 판단됩니다.

  • 단순히 API 스펙에 맞게 변환하는 로직만 들어있어서 책임과 역할이 좋다고 생각합니다 👍
  • record를 사용하면 불필요한 애노테이션을 제거할 수 있습니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

record + @builder 조합으로 변경했습니다!

Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package com.example.app.movie.dto;

import com.example.app.movie.entity.Movie;
import com.example.app.movie.entity.Showtime;
import com.example.app.movie.entity.Theater;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

import java.time.LocalDate;
import java.util.Comparator;
import java.util.List;

@Builder
@Getter
@AllArgsConstructor
public class MovieDto {

private Long id;
private String title;
private String description;
private String rating;
private String genre;
private String thumbnail;
private int runningTime;
private LocalDate releaseDate;
private List<String> theaters;
private List<String> showtimes;

public static MovieDto toDto(Movie movie) {
var showtimes = movie.getShowtimes()
.stream()
.sorted(Comparator.comparing(Showtime::getStart))
.map(showtime -> String.format("%s ~ %s", showtime.getStart(), showtime.getEnd()))
.toList();

var theaters = movie.getTheaters()
.stream()
.map(Theater::getName)
.toList();

return MovieDto.builder()
.id(movie.getId())
.title(movie.getTitle())
.description(movie.getDescription())
.rating(movie.getRating().getDescription())
.genre(movie.getGenre().getDescription())
.thumbnail(movie.getThumbnail())
.runningTime(movie.getRunningTime())
.releaseDate(movie.getReleaseDate())
.theaters(theaters)
.showtimes(showtimes)
.build();
}
}
10 changes: 10 additions & 0 deletions core/src/main/java/com/example/app/movie/dto/SearchMovies.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.example.app.movie.dto;

import jakarta.validation.constraints.NotNull;
import java.time.LocalDate;

public record SearchMovies (
@NotNull(message = "상영 날짜는 필수에요")
LocalDate showDate
){
}
64 changes: 64 additions & 0 deletions core/src/main/java/com/example/app/movie/entity/Movie.java
Copy link

@soonhankwon soonhankwon Jan 10, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

API 요구사항에 맞게 Movie 테이블을 설계하고 엔티티를 만들어주셨습니다 👍
다만 몇 가지 걸리는 점을 보자면,

  • 현재 Setter의 필요성?

    • 당장 사용되지 않는 코드이며, Setter를 사용해서 외부에서 "상태변경"할 가능성이 열려있습니다.
    • 기본적으로 객체 내부에서 상태를 변경하도록 구현하는 것이 코드 재사용성, 캡슐화에 좋다고 생각합니다.
  • OneToMany, ManyToMany가 요구사항을 구현하기 위해 "꼭 필요한가?" 궁금합니다.

    • 저는 해당 애노테이션들은 득보다 실이 훨씬 많다고 생각하는 편입니다.
    • 경험상 해당 애노테이션을 쓰지 않고도 모든 비즈니스 로직을 구현할 수 있습니다.(e.g 중간테이블, join etc)
    • 위 애노테이션(연관관계 특히 양방향)을 사용함으로써 "N+1문제"를 세심하게 고려해줘야하는 가능성이 만들어집니다.
    • entity graph, fetch join, batch size, querydsl 등 방법으로 해결할 수 있지만, 이러한 구조를 애초에 만들지 않는 것도 하나의 방법입니다.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

현재 Setter의 필요성?

OOP 관점에서 말씀해주시는게 좋은거 같습니다.
Setter보다는 도메인 행위(?)로 상태를 수정하는게 더 좋을거라 생각해 Setter 제거했습니다.

경험상 해당 애노테이션을 쓰지 않고도 모든 비즈니스 로직을 구현할 수 있습니다.(e.g 중간테이블, join etc)

일단 N:N 경우 중간테이블 Entity로 만들었습니다. 이걸 어떻게 활용해볼지 고민해보겠습니다.

위 애노테이션(연관관계 특히 양방향)을 사용함으로써 "N+1문제"를 세심하게 고려해줘야하는 가능성이 만들어집니다.

네 맞습니다. 사실 해당 문제 때문에 실무에서도 Entity만 만들고 모든 연관 관계는 querydsl로 해결하고 있습니다.

entity graph, fetch join, batch size, querydsl 등 방법으로 해결할 수 있지만, 이러한 구조를 애초에 만들지 않는 것도 하나의 방법입니다.

👍

Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package com.example.app.movie.entity;

import com.example.app.common.BaseEntity;
import com.example.app.movie.type.MovieGenre;
import com.example.app.movie.type.MovieRating;
import com.example.app.movie.type.MovieStatus;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name="tb_movie")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Movie extends BaseEntity {

@Id
@Column(name = "movie_id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String title;

private String description;

@Enumerated(EnumType.STRING)
private MovieStatus status;

@Enumerated(EnumType.STRING)
private MovieRating rating;

@Enumerated(EnumType.STRING)
private MovieGenre genre;

private String thumbnail;

@Column(name = "running_time")
private int runningTime;

@Column(name = "release_date")
private LocalDate releaseDate;

@OneToMany
@JoinTable(
name = "tb_movie_showtime",
joinColumns = @JoinColumn(name = "movie_id"),
inverseJoinColumns = @JoinColumn(name = "showtime_id"))
private List<Showtime> showtimes = new ArrayList<>();

@ManyToMany
@JoinTable(
name = "tb_movie_theater_rel",
joinColumns = @JoinColumn(name = "movie_id"),
inverseJoinColumns = @JoinColumn(name = "theater_id"))
private List<Theater> theaters = new ArrayList<>();
}
26 changes: 26 additions & 0 deletions core/src/main/java/com/example/app/movie/entity/Showtime.java

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

start, end에 String을 사용하신 이유가 특별히 있으신가요? 저는 LocalTime을 사용하는것이 비즈니스 로직 구현 및 컬럼 타입에 적합해보입니다 :)

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

맞네요 ㅎㅎ LocalTime으로 수정했습니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.example.app.movie.entity;

import com.example.app.common.BaseEntity;
import jakarta.persistence.*;
import lombok.*;

@Entity
@Table(name="tb_movie_showtime")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Showtime extends BaseEntity {

@Id
@Column(name = "showtime_id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

@Column(name = "movie_id")
private Long movieId;

private String start;

private String end;
}
30 changes: 30 additions & 0 deletions core/src/main/java/com/example/app/movie/entity/Theater.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.app.movie.entity;

import com.example.app.common.BaseEntity;
import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

import java.util.ArrayList;
import java.util.List;

@Entity
@Table(name="tb_theater")
@Getter
@Setter
@NoArgsConstructor
@AllArgsConstructor
public class Theater extends BaseEntity {

@Id
@Column(name = "theater_id")
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;

private String name;

@ManyToMany(mappedBy = "theaters")
private List<Movie> movies = new ArrayList<>();
}
17 changes: 17 additions & 0 deletions core/src/main/java/com/example/app/movie/type/MovieGenre.java

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ENUM으로 접근하신 것은 좋은 선택이라고 생각합니다. 👍

  • 한 가지 소개해드릴 방법은 Converter를 사용해서 데이터베이스에 'ACTION'이면 -> 'A' 이렇게 저장해서 데이터베이스 리소스를 절약하는 방법도 있습니다.

Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.example.app.movie.type;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum MovieGenre {
ACTION("액션"),
COMEDY("코미디"),
FAMILY("가족"),
ROMANCE("로맨스"),
HORROR("호러"),
SF("SF");

private final String description;
}
16 changes: 16 additions & 0 deletions core/src/main/java/com/example/app/movie/type/MovieRating.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.example.app.movie.type;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum MovieRating {
ALL_AGES("전체관람가"),
TWELVE_ABOVE("12세 이상 관람가"),
FIFTEEN_ABOVE("15세 이상 관람가"),
NO_MINORS("청소년 관람불가"),
RESTRICTED("제한상영가");

private final String description;
}
Loading
Loading