From 9cd5ff309f24dfeccdb692e20e6dde152c58a519 Mon Sep 17 00:00:00 2001 From: teddy Date: Thu, 9 Apr 2026 17:43:36 +0900 Subject: [PATCH] =?UTF-8?q?=EC=83=98=ED=94=8C=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- api-app/README.md | 32 +++-- api-app/app/build.gradle | 24 ++-- .../DabeeoDetectionApiApplication.java | 3 + .../detection/config/api/ApiResponseDto.java | 40 ++++++ .../detection/db/core/AnimalCoreService.java | 68 ++++++++++ .../cd/detection/db/core/ZooCoreService.java | 74 +++++++++++ .../{core => db/repository/imagery}/.gitkeep | 0 .../repository/inference}/.gitkeep | 0 .../{entity => db/repository/label}/.gitkeep | 0 .../repository/labeling}/.gitkeep | 0 .../inference => db/repository/log}/.gitkeep | 0 .../label => db/repository/model}/.gitkeep | 0 .../repository/system}/.gitkeep | 0 .../db/repository/zoo/AnimalRepository.java | 8 ++ .../zoo/AnimalRepositoryCustom.java | 10 ++ .../repository/zoo/AnimalRepositoryImpl.java | 46 +++++++ .../db/repository/zoo/ZooRepository.java | 8 ++ .../repository/zoo/ZooRepositoryCustom.java | 11 ++ .../db/repository/zoo/ZooRepositoryImpl.java | 45 +++++++ .../domain/zoo/AnimalController.java | 125 ++++++++++++++++++ .../detection/domain/zoo/ZooController.java | 121 +++++++++++++++++ .../detection/domain/zoo/dto/AnimalDto.java | 78 +++++++++++ .../cd/detection/domain/zoo/dto/ZooDto.java | 66 +++++++++ .../domain/zoo/service/AnimalService.java | 32 +++++ .../domain/zoo/service/ZooService.java | 37 ++++++ .../com/cd/detection/entity/AnimalEntity.java | 56 ++++++++ .../com/cd/detection/entity/ZooEntity.java | 62 +++++++++ .../com/cd/detection/repository/log/.gitkeep | 0 .../cd/detection/repository/model/.gitkeep | 0 .../cd/detection/repository/system/.gitkeep | 0 api-app/build.gradle | 38 +++--- .../infrastructure-db-postgres/build.gradle | 4 +- .../repository/.gitkeep | 0 33 files changed, 942 insertions(+), 46 deletions(-) create mode 100644 api-app/app/src/main/java/com/cd/detection/config/api/ApiResponseDto.java create mode 100644 api-app/app/src/main/java/com/cd/detection/db/core/AnimalCoreService.java create mode 100644 api-app/app/src/main/java/com/cd/detection/db/core/ZooCoreService.java rename api-app/app/src/main/java/com/cd/detection/{core => db/repository/imagery}/.gitkeep (100%) rename api-app/app/src/main/java/com/cd/detection/{domain/imagery/controller => db/repository/inference}/.gitkeep (100%) rename api-app/app/src/main/java/com/cd/detection/{entity => db/repository/label}/.gitkeep (100%) rename api-app/app/src/main/java/com/cd/detection/{repository/imagery => db/repository/labeling}/.gitkeep (100%) rename api-app/app/src/main/java/com/cd/detection/{repository/inference => db/repository/log}/.gitkeep (100%) rename api-app/app/src/main/java/com/cd/detection/{repository/label => db/repository/model}/.gitkeep (100%) rename api-app/app/src/main/java/com/cd/detection/{repository/labeling => db/repository/system}/.gitkeep (100%) create mode 100644 api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepository.java create mode 100644 api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepositoryCustom.java create mode 100644 api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepositoryImpl.java create mode 100644 api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepository.java create mode 100644 api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepositoryCustom.java create mode 100644 api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepositoryImpl.java create mode 100644 api-app/app/src/main/java/com/cd/detection/domain/zoo/AnimalController.java create mode 100644 api-app/app/src/main/java/com/cd/detection/domain/zoo/ZooController.java create mode 100644 api-app/app/src/main/java/com/cd/detection/domain/zoo/dto/AnimalDto.java create mode 100644 api-app/app/src/main/java/com/cd/detection/domain/zoo/dto/ZooDto.java create mode 100644 api-app/app/src/main/java/com/cd/detection/domain/zoo/service/AnimalService.java create mode 100644 api-app/app/src/main/java/com/cd/detection/domain/zoo/service/ZooService.java create mode 100644 api-app/app/src/main/java/com/cd/detection/entity/AnimalEntity.java create mode 100644 api-app/app/src/main/java/com/cd/detection/entity/ZooEntity.java delete mode 100644 api-app/app/src/main/java/com/cd/detection/repository/log/.gitkeep delete mode 100644 api-app/app/src/main/java/com/cd/detection/repository/model/.gitkeep delete mode 100644 api-app/app/src/main/java/com/cd/detection/repository/system/.gitkeep delete mode 100644 api-app/infrastructure-db-postgres/repository/.gitkeep diff --git a/api-app/README.md b/api-app/README.md index 81313b0..32f8703 100644 --- a/api-app/README.md +++ b/api-app/README.md @@ -152,7 +152,10 @@ api-app # 메인 실행 모듈 │ │ │ ├── DabeeoDetectionApiApplication.java │ │ │ │ │ │ ├── config # 설정 파일 -│ │ │ ├── core # core service (Repository 비즈니스 서비스) +│ │ │ ├── db +│ │ │ │ ├── core # core service (Repository 비즈니스 서비스) +│ │ │ │ └── repository # Repository (DB 인터페이스/구현) +│ │ │ ├── core │ │ │ ├── domain # 도메인 │ │ │ │ ├── imagery # 영상 데이터 관리 │ │ │ │ ├── labeling # 라벨링 툴 @@ -161,10 +164,7 @@ api-app # 메인 실행 모듈 │ │ │ │ ├── inference # 추론관리 │ │ │ │ ├── system # 시스템관리 │ │ │ │ └── log # 로그관리 -│ │ │ │ │ │ ├── entity # 공용 JPA 엔티티 (DB 공통 사용) -│ │ │ │ -│ │ │ └── repository # 공용 Repository (DB 공통 인터페이스/구현) │ │ │ │ │ └── resources # 설정 및 리소스 파일 │ │ ├── application.yml @@ -174,7 +174,6 @@ api-app # 메인 실행 모듈 │ └── build.gradle │ ├── infrastructure-db-postgres # Postgres 전용 설정/확장 -│ ├── repository # Repository impl 구현 │ └── build.gradle │ ├── build.gradle @@ -215,20 +214,19 @@ DB 관련 로직은 별도의 모듈(infrastructure-db-postgres)로 분리되어 --- -### 🔄 처리 흐름 +### 🔄 Database 교체 -Controller → Service → Repository → DB +- `api-app/build.gradle`에서 DB 모듈 의존성 변경 + (`infrastructure-db-postgres` → `infrastructure-db-oracle`) ---- +- `application.yml` 또는 DB별 설정 파일(`application-postgres.yml`, `application-oracle.yml`)에서 + 데이터베이스 연결 정보 변경 -### 💡 설계 포인트 +- 실행 시 profile을 통해 DB 선택 -- API 모듈은 DB 기술에 직접 의존하지 않음 -- DB 관련 코드는 별도 모듈에서 관리 -- DB 변경 시 API 코드 수정 최소화 +```bash +# PostgreSQL +--spring.profiles.active=postgres ---- - -## 🔥 한 줄 요약 - -DB 기술 변경에 유연하게 대응하기 위해 persistence 레이어를 모듈로 분리했습니다. +# Oracle +--spring.profiles.active=oracle diff --git a/api-app/app/build.gradle b/api-app/app/build.gradle index c3676ea..5841b82 100644 --- a/api-app/app/build.gradle +++ b/api-app/app/build.gradle @@ -1,37 +1,41 @@ plugins { id 'org.springframework.boot' - id 'io.spring.dependency-management' id 'java' } dependencies { - - runtimeOnly 'com.h2database:h2' - - // postgres + // DB 변경시 변경 implementation project(':infrastructure-db-postgres') + // DB (테스트용) + runtimeOnly 'com.h2database:h2' + + // Spring implementation 'org.springframework.boot:spring-boot-starter-web' implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + // OpenAPI implementation 'org.springdoc:springdoc-openapi-starter-webmvc-ui:2.5.0' + + // QueryDSL implementation "com.querydsl:querydsl-jpa:5.0.0:jakarta" + // Lombok compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + + // Devtools developmentOnly 'org.springframework.boot:spring-boot-devtools' - annotationProcessor 'org.projectlombok:lombok' + // QueryDSL APT annotationProcessor "com.querydsl:querydsl-apt:5.0.0:jakarta" annotationProcessor "jakarta.annotation:jakarta.annotation-api" annotationProcessor "jakarta.persistence:jakarta.persistence-api" + // Test testImplementation 'org.springframework.boot:spring-boot-starter-test' } -tasks.named('test') { - useJUnitPlatform() -} - bootJar { archiveFileName = 'ROOT.jar' } \ No newline at end of file diff --git a/api-app/app/src/main/java/com/cd/detection/DabeeoDetectionApiApplication.java b/api-app/app/src/main/java/com/cd/detection/DabeeoDetectionApiApplication.java index fe12666..32e88f5 100644 --- a/api-app/app/src/main/java/com/cd/detection/DabeeoDetectionApiApplication.java +++ b/api-app/app/src/main/java/com/cd/detection/DabeeoDetectionApiApplication.java @@ -2,6 +2,9 @@ package com.cd.detection; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.domain.EntityScan; +import org.springframework.context.annotation.ComponentScan; +import org.springframework.data.jpa.repository.config.EnableJpaRepositories; @SpringBootApplication public class DabeeoDetectionApiApplication { diff --git a/api-app/app/src/main/java/com/cd/detection/config/api/ApiResponseDto.java b/api-app/app/src/main/java/com/cd/detection/config/api/ApiResponseDto.java new file mode 100644 index 0000000..5ae59c7 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/config/api/ApiResponseDto.java @@ -0,0 +1,40 @@ +package com.cd.detection.config.api; + +import com.fasterxml.jackson.annotation.JsonInclude; +import lombok.Getter; +import lombok.ToString; + +@Getter +@ToString +@JsonInclude(JsonInclude.Include.NON_NULL) +public class ApiResponseDto { + + private final T data; + private final Error error; + + private ApiResponseDto(T data, Error error) { + this.data = data; + this.error = error; + } + + // 성공 + public static ApiResponseDto ok(T data) { + return new ApiResponseDto<>(data, null); + } + + // 실패 + public static ApiResponseDto fail(String code, String message) { + return new ApiResponseDto<>(null, new Error(code, message)); + } + + @Getter + public static class Error { + private final String code; + private final String message; + + public Error(String code, String message) { + this.code = code; + this.message = message; + } + } +} diff --git a/api-app/app/src/main/java/com/cd/detection/db/core/AnimalCoreService.java b/api-app/app/src/main/java/com/cd/detection/db/core/AnimalCoreService.java new file mode 100644 index 0000000..21c6176 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/db/core/AnimalCoreService.java @@ -0,0 +1,68 @@ +package com.cd.detection.db.core; + +import com.cd.detection.db.repository.zoo.AnimalRepository; +import com.cd.detection.domain.zoo.dto.AnimalDto; +import com.cd.detection.domain.zoo.dto.AnimalDto.AddRequest; +import com.cd.detection.domain.zoo.dto.AnimalDto.ModifyRequest; +import com.cd.detection.domain.zoo.dto.AnimalDto.SearchListRequest; +import com.cd.detection.entity.AnimalEntity; +import com.cd.detection.entity.ZooEntity; +import jakarta.persistence.EntityNotFoundException; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class AnimalCoreService { + private final AnimalRepository animalRepository; + + @Transactional + public void saveZoo(AddRequest dto) { + ZooEntity zoo = new ZooEntity(); + zoo.setZooId(dto.getZooId()); // FK만 세팅 + + AnimalEntity animalEntity = new AnimalEntity(); + animalEntity.setZoo(zoo); + animalEntity.setAnimalName(dto.getAnimalName()); + animalEntity.setSpecies(dto.getSpecies()); + animalEntity.setAge(dto.getAge()); + + animalRepository.save(animalEntity); + } + + @Transactional + public void updateZoo(ModifyRequest dto) { + AnimalEntity animalEntity = animalRepository.findById(dto.getAnimalId()).orElseThrow(() -> new EntityNotFoundException("Animal Id: " + dto.getAnimalId())); + + animalEntity.setAnimalName(dto.getAnimalName()); + animalEntity.setSpecies(dto.getSpecies()); + animalEntity.setUseYn(dto.getUseYn()); + animalRepository.save(animalEntity); + } + + @Transactional + public void deleteZoo(ModifyRequest dto) { + AnimalEntity animalEntity = animalRepository.findById(dto.getAnimalId()).orElseThrow(() -> new EntityNotFoundException("Animal Id: " + dto.getAnimalId())); + + animalEntity.setDelYn(dto.getDelYn()); + animalRepository.save(animalEntity); + } + + public Page getAnimals(SearchListRequest searchListRequest) { + Page animals = animalRepository.findAllAnimals(searchListRequest); + + return animals.map(a -> { + AnimalDto.Basic res = new AnimalDto.Basic(); + res.setAnimalId(a.getAnimalId()); + res.setAnimalName(a.getAnimalName()); + res.setSpecies(a.getSpecies()); + res.setAge(a.getAge() != null ? a.getAge().toString() : null); + res.setDelYn(a.getDelYn()); + res.setUseYn(a.getUseYn()); + return res; + }); + } +} diff --git a/api-app/app/src/main/java/com/cd/detection/db/core/ZooCoreService.java b/api-app/app/src/main/java/com/cd/detection/db/core/ZooCoreService.java new file mode 100644 index 0000000..d0fc374 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/db/core/ZooCoreService.java @@ -0,0 +1,74 @@ +package com.cd.detection.db.core; + +import com.cd.detection.db.repository.zoo.ZooRepository; +import com.cd.detection.domain.zoo.dto.ZooDto.AddRequest; +import com.cd.detection.domain.zoo.dto.ZooDto.AnimalRes; +import com.cd.detection.domain.zoo.dto.ZooDto.ModifyRequest; +import com.cd.detection.domain.zoo.dto.ZooDto.SearchListRequest; +import com.cd.detection.domain.zoo.dto.ZooDto.ZooWithAnimalsRes; +import com.cd.detection.entity.ZooEntity; +import jakarta.persistence.EntityNotFoundException; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor +public class ZooCoreService { + private final ZooRepository zooRepository; + + @Transactional + public void saveZoo(AddRequest dto) { + ZooEntity zooEntity = new ZooEntity(); + zooEntity.setZooName(dto.getZooName()); + zooEntity.setDescription(dto.getDescription()); + + zooRepository.save(zooEntity); + } + + @Transactional + public void updateZoo(ModifyRequest dto) { + ZooEntity zooEntity = zooRepository.findById(dto.getId()).orElseThrow(() -> new EntityNotFoundException("Zoo Id: " + dto.getId())); + + zooEntity.setZooName(dto.getZooName()); + zooEntity.setDescription(dto.getDescription()); + zooEntity.setUseYn(dto.getUseYn()); + zooRepository.save(zooEntity); + } + + @Transactional + public void deleteZoo(ModifyRequest dto) { + ZooEntity zooEntity = zooRepository.findById(dto.getId()).orElseThrow(() -> new EntityNotFoundException("Zoo Id: " + dto.getId())); + + zooEntity.setDelYn(dto.getDelYn()); + zooRepository.save(zooEntity); + } + + public Page getZooWithAnimals(SearchListRequest searchListRequest) { + Page zoo = zooRepository.findAllWithAnimals(searchListRequest); + + return zoo.map(z -> { + ZooWithAnimalsRes res = new ZooWithAnimalsRes(); + res.setZooId(z.getZooId()); + res.setZooName(z.getZooName()); + res.setDescription(z.getDescription()); + + List animals = z.getAnimals().stream() + .map(a -> { + AnimalRes ar = new AnimalRes(); + ar.setAnimalId(a.getAnimalId()); + ar.setAnimalName(a.getAnimalName()); + ar.setSpecies(a.getSpecies()); + ar.setAge(a.getAge()); + return ar; + }) + .toList(); + + res.setAnimals(animals); + return res; + }); + } +} diff --git a/api-app/app/src/main/java/com/cd/detection/core/.gitkeep b/api-app/app/src/main/java/com/cd/detection/db/repository/imagery/.gitkeep similarity index 100% rename from api-app/app/src/main/java/com/cd/detection/core/.gitkeep rename to api-app/app/src/main/java/com/cd/detection/db/repository/imagery/.gitkeep diff --git a/api-app/app/src/main/java/com/cd/detection/domain/imagery/controller/.gitkeep b/api-app/app/src/main/java/com/cd/detection/db/repository/inference/.gitkeep similarity index 100% rename from api-app/app/src/main/java/com/cd/detection/domain/imagery/controller/.gitkeep rename to api-app/app/src/main/java/com/cd/detection/db/repository/inference/.gitkeep diff --git a/api-app/app/src/main/java/com/cd/detection/entity/.gitkeep b/api-app/app/src/main/java/com/cd/detection/db/repository/label/.gitkeep similarity index 100% rename from api-app/app/src/main/java/com/cd/detection/entity/.gitkeep rename to api-app/app/src/main/java/com/cd/detection/db/repository/label/.gitkeep diff --git a/api-app/app/src/main/java/com/cd/detection/repository/imagery/.gitkeep b/api-app/app/src/main/java/com/cd/detection/db/repository/labeling/.gitkeep similarity index 100% rename from api-app/app/src/main/java/com/cd/detection/repository/imagery/.gitkeep rename to api-app/app/src/main/java/com/cd/detection/db/repository/labeling/.gitkeep diff --git a/api-app/app/src/main/java/com/cd/detection/repository/inference/.gitkeep b/api-app/app/src/main/java/com/cd/detection/db/repository/log/.gitkeep similarity index 100% rename from api-app/app/src/main/java/com/cd/detection/repository/inference/.gitkeep rename to api-app/app/src/main/java/com/cd/detection/db/repository/log/.gitkeep diff --git a/api-app/app/src/main/java/com/cd/detection/repository/label/.gitkeep b/api-app/app/src/main/java/com/cd/detection/db/repository/model/.gitkeep similarity index 100% rename from api-app/app/src/main/java/com/cd/detection/repository/label/.gitkeep rename to api-app/app/src/main/java/com/cd/detection/db/repository/model/.gitkeep diff --git a/api-app/app/src/main/java/com/cd/detection/repository/labeling/.gitkeep b/api-app/app/src/main/java/com/cd/detection/db/repository/system/.gitkeep similarity index 100% rename from api-app/app/src/main/java/com/cd/detection/repository/labeling/.gitkeep rename to api-app/app/src/main/java/com/cd/detection/db/repository/system/.gitkeep diff --git a/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepository.java b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepository.java new file mode 100644 index 0000000..f964c40 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepository.java @@ -0,0 +1,8 @@ +package com.cd.detection.db.repository.zoo; + +import com.cd.detection.entity.AnimalEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface AnimalRepository extends JpaRepository, AnimalRepositoryCustom { + +} diff --git a/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepositoryCustom.java b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepositoryCustom.java new file mode 100644 index 0000000..76cbbe8 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepositoryCustom.java @@ -0,0 +1,10 @@ +package com.cd.detection.db.repository.zoo; + +import com.cd.detection.domain.zoo.dto.AnimalDto; +import com.cd.detection.entity.AnimalEntity; +import java.util.List; +import org.springframework.data.domain.Page; + +public interface AnimalRepositoryCustom { + Page findAllAnimals(AnimalDto.SearchListRequest dto); +} diff --git a/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepositoryImpl.java b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepositoryImpl.java new file mode 100644 index 0000000..29e21f5 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/AnimalRepositoryImpl.java @@ -0,0 +1,46 @@ +package com.cd.detection.db.repository.zoo; + +import com.cd.detection.domain.zoo.dto.AnimalDto.SearchListRequest; +import com.cd.detection.entity.AnimalEntity; +import com.cd.detection.entity.QAnimalEntity; +import com.cd.detection.entity.QZooEntity; +import com.cd.detection.entity.ZooEntity; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.List; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; + +@Repository +public class AnimalRepositoryImpl implements AnimalRepositoryCustom { + private final JPAQueryFactory queryFactory; + + public AnimalRepositoryImpl(JPAQueryFactory queryFactory) { + this.queryFactory = queryFactory; + } + + @Override + public Page findAllAnimals(SearchListRequest searchListRequest) { + Pageable pageable = searchListRequest.toPageable(); + QAnimalEntity animal = QAnimalEntity.animalEntity; + + // content 조회 + List content = queryFactory + .selectFrom(animal) + .where(animal.delYn.eq(false)) + .distinct() + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + // count 조회 (fetchJoin 제거) + Long total = queryFactory + .select(animal.count()) + .from(animal) + .where(animal.delYn.eq(false)) + .fetchOne(); + + return new PageImpl<>(content, pageable, total == null ? 0L : total); + } +} diff --git a/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepository.java b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepository.java new file mode 100644 index 0000000..e28f135 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepository.java @@ -0,0 +1,8 @@ +package com.cd.detection.db.repository.zoo; + +import com.cd.detection.entity.ZooEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface ZooRepository extends JpaRepository, ZooRepositoryCustom { + +} diff --git a/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepositoryCustom.java b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepositoryCustom.java new file mode 100644 index 0000000..7f6f9f7 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepositoryCustom.java @@ -0,0 +1,11 @@ +package com.cd.detection.db.repository.zoo; + +import com.cd.detection.domain.zoo.dto.ZooDto.SearchListRequest; +import com.cd.detection.entity.ZooEntity; +import java.util.List; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageImpl; + +public interface ZooRepositoryCustom { + Page findAllWithAnimals(SearchListRequest searchListRequest); +} diff --git a/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepositoryImpl.java b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepositoryImpl.java new file mode 100644 index 0000000..1280584 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/db/repository/zoo/ZooRepositoryImpl.java @@ -0,0 +1,45 @@ +package com.cd.detection.db.repository.zoo; + +import com.cd.detection.domain.zoo.dto.ZooDto.SearchListRequest; +import com.cd.detection.entity.QZooEntity; +import com.cd.detection.entity.ZooEntity; +import com.querydsl.jpa.impl.JPAQueryFactory; +import java.util.List; +import org.springframework.data.domain.PageImpl; +import org.springframework.data.domain.Pageable; +import org.springframework.stereotype.Repository; + +@Repository +public class ZooRepositoryImpl implements ZooRepositoryCustom { + + private final JPAQueryFactory queryFactory; + + public ZooRepositoryImpl(JPAQueryFactory queryFactory) { + this.queryFactory = queryFactory; + } + + @Override + public PageImpl findAllWithAnimals(SearchListRequest searchListRequest) { + Pageable pageable = searchListRequest.toPageable(); + QZooEntity zoo = QZooEntity.zooEntity; + + // content 조회 + List content = queryFactory + .selectFrom(zoo) + .leftJoin(zoo.animals).fetchJoin() + .where(zoo.delYn.eq(false)) + .distinct() + .offset(pageable.getOffset()) + .limit(pageable.getPageSize()) + .fetch(); + + // count 조회 (fetchJoin 제거) + Long total = queryFactory + .select(zoo.count()) + .from(zoo) + .where(zoo.delYn.eq(false)) + .fetchOne(); + + return new PageImpl<>(content, pageable, total == null ? 0L : total); + } +} diff --git a/api-app/app/src/main/java/com/cd/detection/domain/zoo/AnimalController.java b/api-app/app/src/main/java/com/cd/detection/domain/zoo/AnimalController.java new file mode 100644 index 0000000..d6026cf --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/domain/zoo/AnimalController.java @@ -0,0 +1,125 @@ +package com.cd.detection.domain.zoo; + +import com.cd.detection.config.api.ApiResponseDto; +import com.cd.detection.domain.zoo.dto.AnimalDto; +import com.cd.detection.domain.zoo.service.AnimalService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "동물", description = "동물 API") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/animal") +public class AnimalController { + + private final AnimalService animalService; + + @Operation(summary = "동물 목록", description = "동물 목록 ") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "검색 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Page.class))), + @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @GetMapping("/list") + public ApiResponseDto> getZooWithAnimals( + @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") + @RequestParam(defaultValue = "0") + int page, + @Parameter(description = "페이지 크기", example = "20") + @RequestParam(defaultValue = "20") + int size) { + + AnimalDto.SearchListRequest searchListRequest = new AnimalDto.SearchListRequest(); + searchListRequest.setPage(page); + searchListRequest.setSize(size); + return ApiResponseDto.ok(animalService.getAnimals(searchListRequest)); + } + + @Operation(summary = "동물 등록", description = "동물 등록 ") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "등록 성공", + content = @Content( + mediaType = "text/plain", + schema = @Schema(type = "string", example = "success") + )), + @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @PostMapping + public ApiResponseDto saveAnimal( + @io.swagger.v3.oas.annotations.parameters.RequestBody( + content = @Content( + schema = @Schema(implementation = AnimalDto.AddRequest.class) + ) + ) + @RequestBody AnimalDto.AddRequest dto) { + animalService.saveAnimal(dto); + return ApiResponseDto.ok("success"); + } + + @Operation(summary = "동물 수정", description = "동물 수정 ") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "수정 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @PutMapping + public ApiResponseDto updateAnimal(@RequestBody AnimalDto.ModifyRequest dto) { + animalService.updateAnimal(dto); + return ApiResponseDto.ok("success"); + } + + @Operation(summary = "동물 삭제", description = "동물 삭제 ") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "삭제 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @DeleteMapping + public ApiResponseDto deleteAnimal(@RequestBody AnimalDto.ModifyRequest dto) { + animalService.deleteAnimal(dto); + return ApiResponseDto.ok("success"); + } + + +} diff --git a/api-app/app/src/main/java/com/cd/detection/domain/zoo/ZooController.java b/api-app/app/src/main/java/com/cd/detection/domain/zoo/ZooController.java new file mode 100644 index 0000000..dacb621 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/domain/zoo/ZooController.java @@ -0,0 +1,121 @@ +package com.cd.detection.domain.zoo; + +import com.cd.detection.config.api.ApiResponseDto; +import com.cd.detection.domain.zoo.dto.ZooDto; +import com.cd.detection.domain.zoo.dto.ZooDto.SearchListRequest; +import com.cd.detection.domain.zoo.dto.ZooDto.ZooWithAnimalsRes; +import com.cd.detection.domain.zoo.service.ZooService; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.DeleteMapping; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Tag(name = "동물원", description = "동물원 API") +@RestController +@RequiredArgsConstructor +@RequestMapping("/api/zoo") +public class ZooController { + + private final ZooService zooService; + + @Operation(summary = "동물원 목록", description = "동물원 목록 ") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "검색 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = Page.class))), + @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @GetMapping("/list") + public ApiResponseDto> getZooWithAnimals( + @Parameter(description = "페이지 번호 (0부터 시작)", example = "0") + @RequestParam(defaultValue = "0") + int page, + @Parameter(description = "페이지 크기", example = "20") + @RequestParam(defaultValue = "20") + int size) { + + SearchListRequest searchListRequest = new SearchListRequest(); + searchListRequest.setPage(page); + searchListRequest.setSize(size); + return ApiResponseDto.ok(zooService.getZooWithAnimals(searchListRequest)); + } + + @Operation(summary = "동물원 등록", description = "동물원 등록 ") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "등록 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @PostMapping + public ApiResponseDto saveZoo(@RequestBody ZooDto.AddRequest dto) { + zooService.saveZoo(dto); + return ApiResponseDto.ok("success"); + } + + @Operation(summary = "동물원 수정", description = "동물원 수정 ") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "수정 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @PutMapping + public ApiResponseDto updateZoo(@RequestBody ZooDto.ModifyRequest dto) { + zooService.updateZoo(dto); + return ApiResponseDto.ok("success"); + } + + @Operation(summary = "동물원 삭제", description = "동물원 삭제 ") + @ApiResponses( + value = { + @ApiResponse( + responseCode = "200", + description = "삭제 성공", + content = + @Content( + mediaType = "application/json", + schema = @Schema(implementation = String.class))), + @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) + }) + @DeleteMapping + public ApiResponseDto deleteZoo(@RequestBody ZooDto.ModifyRequest dto) { + zooService.deleteZoo(dto); + return ApiResponseDto.ok("success"); + } + + +} diff --git a/api-app/app/src/main/java/com/cd/detection/domain/zoo/dto/AnimalDto.java b/api-app/app/src/main/java/com/cd/detection/domain/zoo/dto/AnimalDto.java new file mode 100644 index 0000000..3d0a1ef --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/domain/zoo/dto/AnimalDto.java @@ -0,0 +1,78 @@ +package com.cd.detection.domain.zoo.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +public class AnimalDto { + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class SearchListRequest { + // 페이징 파라미터 + private int page = 0; + private int size = 20; + + public Pageable toPageable() { + return PageRequest.of(page, size); + } + } + + @Getter + @Setter + public static class AddRequest { + + @Schema(description = "동물원 id") + private Long zooId; + @Schema(description = "동물명") + private String animalName; + @Schema(description = "종") + private String species; + @Schema(description = "나이") + private Integer age; + } + + @Getter + @Setter + public static class ModifyRequest { + + @Schema(description = "동물 id") + private Long animalId; + @Schema(description = "동물명") + private String animalName; + @Schema(description = "종") + private String species; + @Schema(description = "나이") + private String age; + @Schema(description = "삭제여부") + private Boolean delYn; + @Schema(description = "사용여부") + private Boolean useYn; + + } + + @Getter + @Setter + public static class Basic { + + @Schema(description = "동물 id") + private Long animalId; + @Schema(description = "동물명") + private String animalName; + @Schema(description = "종") + private String species; + @Schema(description = "나이") + private String age; + @Schema(description = "삭제여부") + private Boolean delYn; + @Schema(description = "사용여부") + private Boolean useYn; + + } +} diff --git a/api-app/app/src/main/java/com/cd/detection/domain/zoo/dto/ZooDto.java b/api-app/app/src/main/java/com/cd/detection/domain/zoo/dto/ZooDto.java new file mode 100644 index 0000000..4eb898e --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/domain/zoo/dto/ZooDto.java @@ -0,0 +1,66 @@ +package com.cd.detection.domain.zoo.dto; + +import java.time.LocalDate; +import java.util.List; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; + +public class ZooDto { + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class SearchListRequest { + // 페이징 파라미터 + private int page = 0; + private int size = 20; + + public Pageable toPageable() { + return PageRequest.of(page, size); + } + } + + @Getter + @Setter + public static class AddRequest { + + private String zooName; + private String description; + } + + @Getter + @Setter + public static class ModifyRequest { + + private Long id; + private String zooName; + private String description; + private Boolean useYn; + private Boolean delYn; + } + + @Getter + @Setter + public static class ZooWithAnimalsRes { + + private Long zooId; // 동물원 ID + private String zooName; // 동물원 이름 + private String description; // 설명 + + private List animals; + } + + @Getter + @Setter + public static class AnimalRes { + private Long animalId; + private String animalName; + private String species; + private Integer age; + } +} diff --git a/api-app/app/src/main/java/com/cd/detection/domain/zoo/service/AnimalService.java b/api-app/app/src/main/java/com/cd/detection/domain/zoo/service/AnimalService.java new file mode 100644 index 0000000..80b46ed --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/domain/zoo/service/AnimalService.java @@ -0,0 +1,32 @@ +package com.cd.detection.domain.zoo.service; + +import com.cd.detection.db.core.AnimalCoreService; +import com.cd.detection.domain.zoo.dto.AnimalDto; +import com.cd.detection.domain.zoo.dto.AnimalDto.Basic; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class AnimalService { + + private final AnimalCoreService animalCoreService; + + public Page getAnimals(AnimalDto.SearchListRequest searchListRequest) { + return animalCoreService.getAnimals(searchListRequest); + } + + public void saveAnimal(AnimalDto.AddRequest dto) { + animalCoreService.saveZoo(dto); + } + + public void updateAnimal(AnimalDto.ModifyRequest dto) { + animalCoreService.updateZoo(dto); + } + + public void deleteAnimal(AnimalDto.ModifyRequest dto) { + animalCoreService.deleteZoo(dto); + } + +} diff --git a/api-app/app/src/main/java/com/cd/detection/domain/zoo/service/ZooService.java b/api-app/app/src/main/java/com/cd/detection/domain/zoo/service/ZooService.java new file mode 100644 index 0000000..3ad0e0f --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/domain/zoo/service/ZooService.java @@ -0,0 +1,37 @@ +package com.cd.detection.domain.zoo.service; + +import com.cd.detection.db.core.ZooCoreService; +import com.cd.detection.domain.zoo.dto.ZooDto.AddRequest; +import com.cd.detection.domain.zoo.dto.ZooDto.ModifyRequest; +import com.cd.detection.domain.zoo.dto.ZooDto.SearchListRequest; +import com.cd.detection.domain.zoo.dto.ZooDto.ZooWithAnimalsRes; +import java.util.List; +import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class ZooService { + private final ZooCoreService zooCoreService; + + // 동물원, 동물 조회 + public Page getZooWithAnimals(SearchListRequest searchListRequest) { + return zooCoreService.getZooWithAnimals(searchListRequest); + } + + // 동물원 저장 + public void saveZoo(AddRequest dto) { + zooCoreService.saveZoo(dto); + } + + // 동물원 수정 + public void updateZoo(ModifyRequest dto) { + zooCoreService.updateZoo(dto); + } + + // 동물원 삭제 + public void deleteZoo(ModifyRequest dto) { + zooCoreService.deleteZoo(dto); + } +} diff --git a/api-app/app/src/main/java/com/cd/detection/entity/AnimalEntity.java b/api-app/app/src/main/java/com/cd/detection/entity/AnimalEntity.java new file mode 100644 index 0000000..52e7375 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/entity/AnimalEntity.java @@ -0,0 +1,56 @@ +package com.cd.detection.entity; + +import jakarta.persistence.*; +import java.time.ZonedDateTime; +import lombok.*; +import org.hibernate.annotations.Comment; + +@Entity +@Getter +@Setter +@Table(name = "tb_animal") +@Comment("동물 정보") +public class AnimalEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "animal_id") + @Comment("동물 ID") + private Long animalId; + + /** + * 동물원 엔티티 (FK) + */ + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "zoo_id", nullable = false) + @Comment("동물원 ID (FK)") + private ZooEntity zoo; + + @Column(name = "animal_name", nullable = false) + @Comment("동물 이름") + private String animalName; + + @Column(name = "species") + @Comment("종") + private String species; + + @Column(name = "age") + @Comment("나이") + private Integer age; + + @Column(name = "use_yn", nullable = false) + @Comment("사용 여부") + private Boolean useYn = true; + + @Column(name = "del_yn", nullable = false) + @Comment("삭제 여부") + private Boolean delYn = false; + + @Column(name = "created_dttm", nullable = false) + @Comment("생성 일시") + private ZonedDateTime createdDttm = ZonedDateTime.now(); + + @Column(name = "updated_dttm", nullable = false) + @Comment("수정 일시") + private ZonedDateTime updatedDttm = ZonedDateTime.now(); +} \ No newline at end of file diff --git a/api-app/app/src/main/java/com/cd/detection/entity/ZooEntity.java b/api-app/app/src/main/java/com/cd/detection/entity/ZooEntity.java new file mode 100644 index 0000000..95f3370 --- /dev/null +++ b/api-app/app/src/main/java/com/cd/detection/entity/ZooEntity.java @@ -0,0 +1,62 @@ +package com.cd.detection.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; +import jakarta.persistence.Table; +import java.time.ZonedDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.Comment; + +@Getter +@Setter +@Entity +@Table(name = "tb_zoo") +public class ZooEntity { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "zoo_id") + @Comment("동물원 ID") + private Long zooId; + + @OneToMany(mappedBy = "zoo", fetch = FetchType.LAZY) + private List animals = new ArrayList<>(); + + @Column(name = "zoo_name", nullable = false, length = 200) + @Comment("동물원 이름") + private String zooName; + + @Column(name = "description") + @Comment("설명") + private String description; + + @Column(name = "use_yn", nullable = false) + @Comment("사용 여부") + private Boolean useYn = true; + + @Column(name = "del_yn", nullable = false) + @Comment("삭제 여부") + private Boolean delYn = false; + + @Column(name = "created_dttm", nullable = false, updatable = false) + @Comment("생성 일시") + private ZonedDateTime createdDttm = ZonedDateTime.now(); + + @Column(name = "updated_dttm", nullable = false) + @Comment("수정 일시") + private ZonedDateTime updatedDttm = ZonedDateTime.now(); + + public void addAnimal(AnimalEntity animal) { + animals.add(animal); + animal.setZoo(this); + } +} diff --git a/api-app/app/src/main/java/com/cd/detection/repository/log/.gitkeep b/api-app/app/src/main/java/com/cd/detection/repository/log/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/api-app/app/src/main/java/com/cd/detection/repository/model/.gitkeep b/api-app/app/src/main/java/com/cd/detection/repository/model/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/api-app/app/src/main/java/com/cd/detection/repository/system/.gitkeep b/api-app/app/src/main/java/com/cd/detection/repository/system/.gitkeep deleted file mode 100644 index e69de29..0000000 diff --git a/api-app/build.gradle b/api-app/build.gradle index f2de0e6..607f5a2 100644 --- a/api-app/build.gradle +++ b/api-app/build.gradle @@ -1,26 +1,30 @@ plugins { - id 'java' - id 'io.spring.dependency-management' version '1.1.7' apply false id 'org.springframework.boot' version '3.5.7' apply false + id 'io.spring.dependency-management' version '1.1.7' apply false } -group = 'com.cd.detection' -version = '0.0.1-SNAPSHOT' - -java { - toolchain { - languageVersion = JavaLanguageVersion.of(21) - } -} - -repositories { - mavenCentral() -} - -subprojects { - apply plugin: 'java' +allprojects { + group = 'com.cd.detection' + version = '0.0.1-SNAPSHOT' repositories { mavenCentral() } +} + +subprojects { + apply plugin: 'java' + apply plugin: 'io.spring.dependency-management' + + java { + toolchain { + languageVersion = JavaLanguageVersion.of(21) + } + } + + dependencyManagement { + imports { + mavenBom "org.springframework.boot:spring-boot-dependencies:3.5.7" + } + } } \ No newline at end of file diff --git a/api-app/infrastructure-db-postgres/build.gradle b/api-app/infrastructure-db-postgres/build.gradle index a2eb670..51fa63e 100644 --- a/api-app/infrastructure-db-postgres/build.gradle +++ b/api-app/infrastructure-db-postgres/build.gradle @@ -3,6 +3,6 @@ plugins { } dependencies { - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' - runtimeOnly 'org.postgresql:postgresql' + // DB 드라이버 + implementation 'org.postgresql:postgresql:42.7.3' } \ No newline at end of file diff --git a/api-app/infrastructure-db-postgres/repository/.gitkeep b/api-app/infrastructure-db-postgres/repository/.gitkeep deleted file mode 100644 index e69de29..0000000