diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..fe91377 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,34 @@ +FROM adoptopenjdk/openjdk11:latest as BUILD_IMAGE + +ENV WORK_DIR=/usr/app/ + +# app 작업 디렉토리 설정 +WORKDIR $WORK_DIR + +# gradle 실행을 위한 필수 디렉토리 준비 +COPY gradlew $WORK_DIR +COPY build.gradle $WORK_DIR +COPY settings.gradle $WORK_DIR +COPY gradle $WORK_DIR/gradle + +RUN ./gradlew -x test build || return 0 + +COPY src src + +# jar 파일 build +RUN ./gradlew bootjar + +FROM adoptopenjdk/openjdk11:latest + +ENV WORK_DIR=/usr/app/ + +WORKDIR $WORK_DIR + +COPY --from=BUILD_IMAGE $WORK_DIR/build/libs/*.jar app-server.jar + +ENTRYPOINT ["java", \ +"-jar", \ +"-Dspring.profiles.active=${PROFILE_OPTION}", \ +"-Dspring.datasource.url=${SPRING_DATASOURCE_URL}", \ +"-Dredis.host=${REDIS_HOST}", \ +"app-server.jar"] diff --git a/build.gradle b/build.gradle index f7f413f..4a38930 100644 --- a/build.gradle +++ b/build.gradle @@ -22,14 +22,28 @@ 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' + + // log4j2 + implementation 'org.springframework.boot:spring-boot-starter-log4j2' + testImplementation 'org.springframework.boot:spring-boot-starter-log4j2' + compileOnly 'org.projectlombok:lombok' developmentOnly 'org.springframework.boot:spring-boot-devtools' - runtimeOnly 'com.h2database:h2' + + // MySQL runtimeOnly 'mysql:mysql-connector-java' + annotationProcessor 'org.projectlombok:lombok' testImplementation 'org.springframework.boot:spring-boot-starter-test' } +configurations { + all { + // log4j2 적용을 위해 기존 spring boot에서 제공하는 logging exclude + exclude group: 'org.springframework.boot', module: 'spring-boot-starter-logging' + } +} + test { useJUnitPlatform() } diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..f94d16a --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,50 @@ +version: "3.8" +services: + nginx: + image: nginx + container_name: nginx + ports: + - "80:80" + volumes: + - ./nginx/:/etc/nginx/conf.d/ + depends_on: + - app-server-1 + - app-server-2 + app-server-1: + build: + context: . + dockerfile: Dockerfile + env_file: + - env/app.env + depends_on: + - db-mysql + app-server-2: + build: + context: . + dockerfile: Dockerfile + env_file: + - env/app.env + depends_on: + - db-mysql + migration: + image: flyway/flyway:7.5.1 + command: -configFiles=/flyway/conf/flyway.config -locations=filesystem:/flyway/sql -connectRetries=60 migrate + volumes: + - ${PWD}/flyway/db-migration/main:/flyway/sql + - ${PWD}/flyway/conf/flyway_main.conf:/flyway/conf/flyway.config + depends_on: + - db-mysql + seed: + image: flyway/flyway:7.5.1 + command: -configFiles=/flyway/conf/flyway.config -locations=filesystem:/flyway/sql -connectRetries=60 migrate + volumes: + - ${PWD}/flyway/db-migration/seed:/flyway/sql + - ${PWD}/flyway/conf/flyway_seed.conf:/flyway/conf/flyway.config + depends_on: + - db-mysql + db-mysql: + image: mysql:5.7.34 + ports: + - "3306:3306" + env_file: + - env/mysql.env diff --git a/env/app.env b/env/app.env new file mode 100644 index 0000000..11ba032 --- /dev/null +++ b/env/app.env @@ -0,0 +1,4 @@ +PROFILE_OPTION=dev +SPRING_DATASOURCE_URL=jdbc:mysql://db-mysql:3306/dongnecafe?autoreconnect=true&characterEncoding=utf8&serverTimezone=Asia/Seoul +SPRING_DATASOURCE_USERNAME=root +SPRING_DATASOURCE_PASSWORD=dev diff --git a/env/mysql.env b/env/mysql.env new file mode 100644 index 0000000..e744bc7 --- /dev/null +++ b/env/mysql.env @@ -0,0 +1,2 @@ +MYSQL_DATABASE=dongnecafe +MYSQL_ROOT_PASSWORD=dev \ No newline at end of file diff --git a/flyway/Dockerfile b/flyway/Dockerfile new file mode 100644 index 0000000..a584418 --- /dev/null +++ b/flyway/Dockerfile @@ -0,0 +1,7 @@ +FROM flyway/flyway + +COPY conf/flyway_main.conf main.conf +COPY conf/flyway_seed.conf seed.conf + +ENTRYPOINT ["-configFiles=main.conf", "migrate"] +ENTRYPOINT ["-configFiles=seed.conf", "migrate"] \ No newline at end of file diff --git a/flyway/conf/flyway_main.conf b/flyway/conf/flyway_main.conf new file mode 100644 index 0000000..4eeb2e8 --- /dev/null +++ b/flyway/conf/flyway_main.conf @@ -0,0 +1,4 @@ +flyway.url=jdbc:mysql://db-mysql:3306/dongnecafe +flyway.user=root +flyway.password=dev +flyway.driver=com.mysql.cj.jdbc.Driver \ No newline at end of file diff --git a/flyway/conf/flyway_seed.conf b/flyway/conf/flyway_seed.conf new file mode 100644 index 0000000..4eeb2e8 --- /dev/null +++ b/flyway/conf/flyway_seed.conf @@ -0,0 +1,4 @@ +flyway.url=jdbc:mysql://db-mysql:3306/dongnecafe +flyway.user=root +flyway.password=dev +flyway.driver=com.mysql.cj.jdbc.Driver \ No newline at end of file diff --git a/nginx/default.conf b/nginx/default.conf new file mode 100644 index 0000000..4e0f6a9 --- /dev/null +++ b/nginx/default.conf @@ -0,0 +1,10 @@ +upstream appserver { + server app-server-1:8080; + server app-server-2:8080; +} +server { + listen 80; + location / { + proxy_pass http://appserver; + } +} \ No newline at end of file diff --git a/src/main/java/io/beaniejoy/dongecafe/DongneCafeSirenOrderApplication.java b/src/main/java/io/beaniejoy/dongnecafe/DongneCafeSirenOrderApplication.java similarity index 90% rename from src/main/java/io/beaniejoy/dongecafe/DongneCafeSirenOrderApplication.java rename to src/main/java/io/beaniejoy/dongnecafe/DongneCafeSirenOrderApplication.java index 4fcfdf4..4599b04 100644 --- a/src/main/java/io/beaniejoy/dongecafe/DongneCafeSirenOrderApplication.java +++ b/src/main/java/io/beaniejoy/dongnecafe/DongneCafeSirenOrderApplication.java @@ -1,4 +1,4 @@ -package io.beaniejoy.dongecafe; +package io.beaniejoy.dongnecafe; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; diff --git a/src/main/java/io/beaniejoy/dongnecafe/cafe/controller/CafeController.java b/src/main/java/io/beaniejoy/dongnecafe/cafe/controller/CafeController.java new file mode 100644 index 0000000..66a6683 --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/cafe/controller/CafeController.java @@ -0,0 +1,39 @@ +package io.beaniejoy.dongnecafe.cafe.controller; + +import io.beaniejoy.dongnecafe.cafe.dto.CafeResponseDto; +import io.beaniejoy.dongnecafe.cafe.service.CafeService; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.util.List; +import java.util.UUID; + +@RestController +@RequiredArgsConstructor +@RequestMapping("/cafes") +public class CafeController { + + private final CafeService cafeService; + + @GetMapping("/") + public ResponseEntity> getCafeList( + @PageableDefault(sort = "name", direction = Sort.Direction.ASC, size = 10) Pageable pageable) { + List cafeResponseList = cafeService.getCafeList(pageable); + + return ResponseEntity.ok(cafeResponseList); + } + + @GetMapping("/{cafeId}") + public ResponseEntity getCafeInfo(@PathVariable("cafeId") UUID cafeId) { + CafeResponseDto cafeResponse = cafeService.getCafeByCafeId(cafeId); + + return ResponseEntity.ok(cafeResponse); + } +} diff --git a/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/Cafe.java b/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/Cafe.java new file mode 100644 index 0000000..e9f739b --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/Cafe.java @@ -0,0 +1,54 @@ +package io.beaniejoy.dongnecafe.cafe.domain; + +import io.beaniejoy.dongnecafe.cafe.dto.CafeResponseDto; +import io.beaniejoy.dongnecafe.common.domain.BaseTimeEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.util.List; +import java.util.UUID; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Cafe extends BaseTimeEntity { + + @Id + @GeneratedValue(generator = "uuid2") + @GenericGenerator(name = "uuid2", strategy = "uuid2") + @Column(name = "cafe_id", columnDefinition = "BINARY(16)") + private UUID cafeId; + + private String name; + + private String address; + + private String phoneNumber; + + private Double totalRate; + + private String description; + + @OneToMany(mappedBy = "cafe", fetch = FetchType.LAZY, cascade = CascadeType.ALL) + private List cafeMenuList; + + @OneToMany(mappedBy = "cafe", fetch = FetchType.LAZY, cascade = CascadeType.ALL) + private List cafeImageList; + + public CafeResponseDto toResponseDto() { + return CafeResponseDto.builder() + .cafeId(cafeId) + .name(name) + .address(address) + .phoneNumber(phoneNumber) + .totalRate(totalRate) + .description(description) + .build(); + } +} diff --git a/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/CafeImage.java b/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/CafeImage.java new file mode 100644 index 0000000..1207922 --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/CafeImage.java @@ -0,0 +1,31 @@ +package io.beaniejoy.dongnecafe.cafe.domain; + +import io.beaniejoy.dongnecafe.common.domain.BaseTimeEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.util.UUID; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class CafeImage extends BaseTimeEntity { + + @Id + @GeneratedValue(generator = "uuid2") + @GenericGenerator(name = "uuid2", strategy = "uuid2") + @Column(name = "cafe_img_id", columnDefinition = "BINARY(16)") + private UUID cafeImgId; + + private String imgUrl; + + @ManyToOne + @JoinColumn(name = "cafe_id") + private Cafe cafe; +} diff --git a/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/CafeMenu.java b/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/CafeMenu.java new file mode 100644 index 0000000..4227d0d --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/CafeMenu.java @@ -0,0 +1,37 @@ +package io.beaniejoy.dongnecafe.cafe.domain; + +import io.beaniejoy.dongnecafe.common.domain.BaseTimeEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.util.List; +import java.util.UUID; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class CafeMenu extends BaseTimeEntity { + + @Id + @GeneratedValue(generator = "uuid2") + @GenericGenerator(name = "uuid2", strategy = "uuid2") + @Column(name = "menu_id", columnDefinition = "BINARY(16)") + private UUID menuId; + + private String name; + + private Integer price; + + @ManyToOne + @JoinColumn(name = "cafe_id") + private Cafe cafe; + + @OneToMany(mappedBy = "cafeMenu") + private List menuOptionList; +} diff --git a/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/MenuOption.java b/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/MenuOption.java new file mode 100644 index 0000000..0f5dcd9 --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/cafe/domain/MenuOption.java @@ -0,0 +1,31 @@ +package io.beaniejoy.dongnecafe.cafe.domain; + +import io.beaniejoy.dongnecafe.common.domain.BaseTimeEntity; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.hibernate.annotations.GenericGenerator; + +import javax.persistence.*; +import java.util.UUID; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class MenuOption extends BaseTimeEntity { + + @Id + @GeneratedValue(generator = "uuid2") + @GenericGenerator(name = "uuid2", strategy = "uuid2") + @Column(name = "option_id", columnDefinition = "BINARY(16)") + private UUID optionId; + + private String title; + + @ManyToOne + @JoinColumn(name = "menu_id") + private CafeMenu cafeMenu; +} diff --git a/src/main/java/io/beaniejoy/dongnecafe/cafe/dto/CafeResponseDto.java b/src/main/java/io/beaniejoy/dongnecafe/cafe/dto/CafeResponseDto.java new file mode 100644 index 0000000..5846432 --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/cafe/dto/CafeResponseDto.java @@ -0,0 +1,27 @@ +package io.beaniejoy.dongnecafe.cafe.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import java.util.UUID; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class CafeResponseDto { + + private UUID cafeId; + + private String name; + + private String address; + + private String phoneNumber; + + private Double totalRate; + + private String description; +} diff --git a/src/main/java/io/beaniejoy/dongnecafe/cafe/error/CafeExceptionHandler.java b/src/main/java/io/beaniejoy/dongnecafe/cafe/error/CafeExceptionHandler.java new file mode 100644 index 0000000..d5f5120 --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/cafe/error/CafeExceptionHandler.java @@ -0,0 +1,14 @@ +package io.beaniejoy.dongnecafe.cafe.error; + +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class CafeExceptionHandler { + + @ExceptionHandler(CafeNotFoundException.class) + public ResponseEntity handleNotFound(CafeNotFoundException exception) { + return ResponseEntity.badRequest().body(exception.getMessage()); + } +} diff --git a/src/main/java/io/beaniejoy/dongnecafe/cafe/error/CafeNotFoundException.java b/src/main/java/io/beaniejoy/dongnecafe/cafe/error/CafeNotFoundException.java new file mode 100644 index 0000000..8a1d222 --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/cafe/error/CafeNotFoundException.java @@ -0,0 +1,9 @@ +package io.beaniejoy.dongnecafe.cafe.error; + +import java.util.UUID; + +public class CafeNotFoundException extends RuntimeException { + public CafeNotFoundException(UUID cafeId) { + super("Cafe[" + cafeId + "] is not found"); + } +} diff --git a/src/main/java/io/beaniejoy/dongnecafe/cafe/repository/CafeRepository.java b/src/main/java/io/beaniejoy/dongnecafe/cafe/repository/CafeRepository.java new file mode 100644 index 0000000..64e2f35 --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/cafe/repository/CafeRepository.java @@ -0,0 +1,13 @@ +package io.beaniejoy.dongnecafe.cafe.repository; + +import io.beaniejoy.dongnecafe.cafe.domain.Cafe; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.repository.JpaRepository; + +import java.util.UUID; + +public interface CafeRepository extends JpaRepository { + + Page findAll(Pageable pageable); +} diff --git a/src/main/java/io/beaniejoy/dongnecafe/cafe/service/CafeService.java b/src/main/java/io/beaniejoy/dongnecafe/cafe/service/CafeService.java new file mode 100644 index 0000000..4f5b2b2 --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/cafe/service/CafeService.java @@ -0,0 +1,38 @@ +package io.beaniejoy.dongnecafe.cafe.service; + +import io.beaniejoy.dongnecafe.cafe.domain.Cafe; +import io.beaniejoy.dongnecafe.cafe.dto.CafeResponseDto; +import io.beaniejoy.dongnecafe.cafe.error.CafeNotFoundException; +import io.beaniejoy.dongnecafe.cafe.repository.CafeRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Service; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class CafeService { + + private final CafeRepository cafeRepository; + + public List getCafeList(Pageable pageable) { + + Page cafeListWithPagination = cafeRepository.findAll(pageable); + + return cafeListWithPagination.stream() + .map(Cafe::toResponseDto) + .collect(Collectors.toList()); + } + + public CafeResponseDto getCafeByCafeId(UUID cafeId) { + Cafe cafe = cafeRepository.findById(cafeId) + .orElseThrow(() -> new CafeNotFoundException(cafeId)); + + return cafe.toResponseDto(); + } +} diff --git a/src/main/java/io/beaniejoy/dongnecafe/common/config/AuditingConfig.java b/src/main/java/io/beaniejoy/dongnecafe/common/config/AuditingConfig.java new file mode 100644 index 0000000..34d8dd2 --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/common/config/AuditingConfig.java @@ -0,0 +1,9 @@ +package io.beaniejoy.dongnecafe.common.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.data.jpa.repository.config.EnableJpaAuditing; + +@Configuration +@EnableJpaAuditing +public class AuditingConfig { +} diff --git a/src/main/java/io/beaniejoy/dongnecafe/common/domain/BaseTimeEntity.java b/src/main/java/io/beaniejoy/dongnecafe/common/domain/BaseTimeEntity.java new file mode 100644 index 0000000..7da0e4d --- /dev/null +++ b/src/main/java/io/beaniejoy/dongnecafe/common/domain/BaseTimeEntity.java @@ -0,0 +1,21 @@ +package io.beaniejoy.dongnecafe.common.domain; + +import lombok.Getter; +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.annotation.LastModifiedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import javax.persistence.EntityListeners; +import javax.persistence.MappedSuperclass; +import java.time.LocalDateTime; + +@Getter +@MappedSuperclass +@EntityListeners(AuditingEntityListener.class) +public abstract class BaseTimeEntity { + @CreatedDate + private LocalDateTime createdDate; + + @LastModifiedDate + private LocalDateTime updatedDate; +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 8b13789..0000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..07335ad --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,6 @@ +spring: + jpa: + properties: + hibernate: + dialect: org.hibernate.dialect.MySQL5InnoDBDialect + show-sql: true \ No newline at end of file diff --git a/src/test/java/io/beaniejoy/dongecafe/DongneCafeSirenOrderApplicationTests.java b/src/test/java/io/beaniejoy/dongnecafe/DongneCafeSirenOrderApplicationTests.java similarity index 86% rename from src/test/java/io/beaniejoy/dongecafe/DongneCafeSirenOrderApplicationTests.java rename to src/test/java/io/beaniejoy/dongnecafe/DongneCafeSirenOrderApplicationTests.java index f33415a..5d933b0 100644 --- a/src/test/java/io/beaniejoy/dongecafe/DongneCafeSirenOrderApplicationTests.java +++ b/src/test/java/io/beaniejoy/dongnecafe/DongneCafeSirenOrderApplicationTests.java @@ -1,4 +1,4 @@ -package io.beaniejoy.dongecafe; +package io.beaniejoy.dongnecafe; import org.junit.jupiter.api.Test; import org.springframework.boot.test.context.SpringBootTest;