Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
a7e60d0
feat(config): add AsyncConfig with s3UploadExecutor thread pool
Jjiggu Jun 4, 2025
71155f8
feat(config): add AwsS3Config for AwsS3Client bean configuration
Jjiggu Jun 4, 2025
68a5c2b
chore(build): add AWS S3 and Resilience4j depende
Jjiggu Jun 4, 2025
27a8dad
feat(s3): implements S3Service with async upload and delete method
Jjiggu Jun 4, 2025
7624cb6
chore(security): permit all requests to /stores/**
Jjiggu Jun 4, 2025
57d554c
feat(store-images): add StoreImage entity
Jjiggu Jun 4, 2025
cfb4cfb
feat(store-images): add endpoints for uploading and deleting store im…
Jjiggu Jun 4, 2025
95c2b71
feat(store-images): add StoreImageRepository
Jjiggu Jun 4, 2025
eb1ee68
feat(store-images): add StoreImageService for batch upload and delete…
Jjiggu Jun 4, 2025
bf8a1de
feat(store-images): add StoreImageUploadResponse dto with fromEntity …
Jjiggu Jun 4, 2025
2be16e4
refactor(store): remove setters, imageUrl, and add update methods
Jjiggu Jun 4, 2025
05f9bb6
refactor(store): remove imageUrl field
Jjiggu Jun 4, 2025
efa11ea
refactor(store): remove imageUrl, add List<StoreImageUploadResponse>
Jjiggu Jun 4, 2025
c2f4ffb
refactor(store): rename fromEntity to of in StoreReadResponse
Jjiggu Jun 4, 2025
55a5b5d
feat(store): include store image information in StoreReadDto mapping
Jjiggu Jun 4, 2025
455f6bf
refactor(store): remove imageUrl
Jjiggu Jun 4, 2025
b38796b
Merge pull request #15 from GTable/feature/#14-image-upload
Jjiggu Jun 4, 2025
651c8d5
feat(store-images): add file count and file size validation for uploads
Jjiggu Jun 4, 2025
26cdf37
refactor(store-image): improve async synchronization and transaction …
Jjiggu Jun 4, 2025
b241273
chore(build): update AWS SDK dependencies to newer versions
Jjiggu Jun 4, 2025
c7b4e21
Merge pull request #17 from GTable/feature/#14-image-upload
Jjiggu Jun 4, 2025
6c1c4ed
chore(build): update AWS SDK dependencies to newer versions
Jjiggu Jun 6, 2025
edb22bc
feat(menu): add Menu Entity
Jjiggu Jun 6, 2025
ee04a42
feat(menu): add endpoints for create, get Menu
Jjiggu Jun 6, 2025
95ab00f
feat(menu): add MenuCreateRequest dto with toEntity method
Jjiggu Jun 6, 2025
ddd9263
feat(menu): add MenuCreateResponse dto with fromEntity method
Jjiggu Jun 6, 2025
04da6f2
feat(menu): add MenuReadDto dto with fromEntity method
Jjiggu Jun 6, 2025
a41dbe5
feat(menu): add MenuReadResponse dto with of method
Jjiggu Jun 6, 2025
dbb5e9f
feat(menu): add MenuRepository with findAllByStoreId method
Jjiggu Jun 6, 2025
687c92f
feat(menu): MenuService with create and read methods including image …
Jjiggu Jun 6, 2025
2815b87
feat(menu-image): add MenuImage entity with BaseTimeEntity and relati…
Jjiggu Jun 6, 2025
09f0e01
feat(menu-image): add endpoints for upload, delete Menu Images
Jjiggu Jun 6, 2025
01f21f8
feat(menu-image): add MenuImageRepository with findByMenu method
Jjiggu Jun 6, 2025
86a5215
feat(menu-image): add MenuImageService for upload and delete operations
Jjiggu Jun 6, 2025
38ed521
feat(menu-image): add MenuImageUploadResponse dto with fromEntity fac…
Jjiggu Jun 6, 2025
35cb4fb
refactor(s3): add type parameter to createFileKey for generic file ke…
Jjiggu Jun 6, 2025
13eace4
chore(security): temporarily permit all requests to /menus/**
Jjiggu Jun 6, 2025
5bee7f4
refactor(s3): add type parameter to createFileKey for generic file ke…
Jjiggu Jun 6, 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
8 changes: 8 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,14 @@ dependencies {
// Validation
implementation 'org.springframework.boot:spring-boot-starter-validation'

// S3
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
// Resilience4j
implementation 'io.github.resilience4j:resilience4j-spring-boot2:1.7.1'
implementation 'io.github.resilience4j:resilience4j-bulkhead:1.7.1'
// 비동기 실행
implementation 'org.springframework.boot:spring-boot-starter-aop'

Comment on lines +54 to +61
Copy link

Choose a reason for hiding this comment

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

💡 Verification agent

🧩 Analysis chain

새로 추가된 의존성들의 버전 호환성을 검증해주세요.

새로 추가하신 의존성들이 메뉴 이미지 업로드 기능을 위한 것으로 보이는데, 몇 가지 검토가 필요한 부분이 있습니다:

  1. spring-cloud-starter-aws:2.2.6.RELEASE는 Spring Boot 3.x와 호환성 문제가 있을 수 있습니다.
  2. resilience4j-spring-boot2:1.7.1는 Spring Boot 2 버전용입니다.

다음 스크립트로 최신 호환 버전을 확인해보세요:


🌐 Web query:

Spring Boot 3.5.0과 호환되는 Spring Cloud AWS 의존성 최신 버전은 무엇인가요?

💡 Result:

Spring Boot 3.5.0과 호환되는 Spring Cloud AWS의 최신 버전에 대해 알아보겠습니다.

Spring Boot 3.5.0이 최근 2025년 5월 22일에 출시되었으며, 이에 맞는 Spring Cloud AWS 버전을 확인해야 합니다[2]. Spring Cloud의 릴리스 트레인을 살펴보면, Spring Boot 3.5.x는 Spring Cloud 2025.0.x(코드명: Northfields)와 호환됩니다[3].

Spring Cloud AWS는 AWS 서비스와 Spring 애플리케이션의 통합을 쉽게 해주는 프로젝트로, 현재 커뮤니티에서 운영되고 있습니다[6]. 이 프로젝트는 AWS가 제공하는 Java SDK를 사용하지만, Spring 기반 모듈을 통합하여 AWS 서비스를 더 편리하게 사용할 수 있게 해줍니다[6].

Spring Cloud AWS 버전 호환성 정보에 따르면:

  1. Spring Cloud AWS 3.0.0 이상 버전은 Spring Boot 3.0 이상과 호환됩니다[6].
  2. 2024년 6월에 Spring Cloud AWS 3.2.0이 출시되었으며, 이는 Spring Boot 3.2.x 및 3.3.x와 호환됩니다[7].
  3. Spring Boot 3.4.x와 호환되는 Spring Cloud AWS 버전을 위한 업그레이드 레시피도 존재합니다[4].

Spring Boot 3.5.0을 위한 Spring Cloud AWS의 최신 호환 버전은 아직 명시적으로 언급되어 있지 않지만, Spring Cloud 2025.0.x(Northfields) 릴리스 트레인에 맞는 Spring Cloud AWS 버전이 필요할 것입니다. 현재 기준으로는 Spring Cloud AWS 3.3.0 이상의 버전이 필요할 것으로 예상됩니다.

사용 방법:

Spring Cloud AWS를 사용하려면 다음과 같이 의존성을 추가해야 합니다:

implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3:3.3.0' // 또는 최신 버전

Maven의 경우:

<dependency>
    <groupId>io.awspring.cloud</groupId>
    <artifactId>spring-cloud-aws-starter-s3</artifactId>
    <version>3.3.0</version> <!-- 또는 최신 버전 -->
</dependency>

Spring Cloud AWS의 장점은 AWS 자격 증명을 자동으로 처리해주는 것으로, application.yml에 다음과 같이 설정만 해주면 됩니다[6]:

spring:
  cloud:
    aws:
      credentials:
        access-key: ${AWS_ACCESS_KEY}
        secret-key: ${AWS_SECRET_KEY}
      s3:
        bucket: ${AWS_S3_BUCKET}
      region:
        static: ${AWS_S3_REGION}

정확한 최신 버전 정보는 Spring Cloud AWS 공식 GitHub 저장소(https://github.com/awspring/spring-cloud-aws)에서 확인하는 것이 좋습니다[1].

Citations:


🌐 Web query:

Spring Boot 3.x와 호환되는 Resilience4j 최신 버전은 무엇인가요?

💡 Result:

Spring Boot 3.x와 호환되는 Resilience4j 최신 버전

Spring Boot 3.x를 사용할 때는 반드시 이에 맞는 Resilience4j 아티팩트와 버전을 선택해야 합니다. Spring Boot 2.x와는 별도의 지원 라이브러리가 존재하며, 호환성 문제를 피하기 위해 다음 사항을 참고하세요.

적용 라이브러리

  • Spring Boot 2.x: resilience4j-spring-boot2
  • Spring Boot 3.x: resilience4j-spring-boot3[1][6]

최신 버전 정보

  • 2025년 6월 기준, Resilience4j의 최신 버전은 2.3.0입니다. 이 버전에서는 Spring Boot 3 및 Spring Framework 6을 지원하며, 이를 위해 resilience4j-spring-boot3resilience4j-spring6 모듈이 추가되었습니다[8].

예시 의존성 설정 (Gradle)

implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.3.0'
implementation 'org.springframework.boot:spring-boot-starter-actuator'
implementation 'org.springframework.boot:spring-boot-starter-aop'

[6][8]

버전 호환 표

Spring Boot 버전 권장 Resilience4j 아티팩트 최신 권장 버전
2.x resilience4j-spring-boot2 2.x.x
3.x resilience4j-spring-boot3 2.3.0

주의사항

  • Spring Cloud를 함께 사용하는 경우, Spring Cloud도 Spring Boot 3.x와 호환되는 버전으로 올려야 Resilience4j가 정상 동작합니다. 구버전 Spring Cloud 의존성으로 인해 이전 Resilience4j 버전이 강제로 사용되는 문제가 발생할 수 있습니다[1].
  • 공식 릴리스 노트나 프로젝트 사이트에서 지원 여부와 버전 호환을 확인하는 것이 좋습니다.

결론

  • Spring Boot 3.x와 호환되는 Resilience4j의 최신 버전은 2.3.0이며, resilience4j-spring-boot3 아티팩트를 사용해야 합니다[6][8].

Citations:


build.gradle(54–61) 의존성 호환성 점검 및 업데이트 필요

아래 항목을 Spring Boot 3.5.0, Spring Cloud 2025.0.x, Spring Framework 6 환경에 맞춰 수정해주세요:

  • AWS S3
    • 기존:
    implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
    • 권장:
    implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3:3.3.0' // 또는 최신 버전
  • Resilience4j
    • 기존:
    implementation 'io.github.resilience4j:resilience4j-spring-boot2:1.7.1'
    implementation 'io.github.resilience4j:resilience4j-bulkhead:1.7.1'
    • 권장:
    implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.3.0'
    implementation 'io.github.resilience4j:resilience4j-bulkhead:2.3.0'
  • 비동기 실행(AOP)은 현행 유지해도 무방합니다.
--- a/build.gradle
+++ b/build.gradle
@@ -54,8 +54,8 @@
-   implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'
+   implementation 'io.awspring.cloud:spring-cloud-aws-starter-s3:3.3.0'
-   implementation 'io.github.resilience4j:resilience4j-spring-boot2:1.7.1'
-   implementation 'io.github.resilience4j:resilience4j-bulkhead:1.7.1'
+   implementation 'io.github.resilience4j:resilience4j-spring-boot3:2.3.0'
+   implementation 'io.github.resilience4j:resilience4j-bulkhead:2.3.0'
    implementation 'org.springframework.boot:spring-boot-starter-aop'

변경 후 빌드·테스트가 정상 수행되는지 확인 부탁드립니다.

🤖 Prompt for AI Agents
In build.gradle lines 54 to 61, the added dependencies are incompatible with
Spring Boot 3.5.0 and Spring Cloud 2025.0.x. Replace
'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' with
'io.awspring.cloud:spring-cloud-aws-starter-s3:3.3.0' or a newer compatible
version. Update Resilience4j dependencies from 'resilience4j-spring-boot2:1.7.1'
and 'resilience4j-bulkhead:1.7.1' to 'resilience4j-spring-boot3:2.3.0' and
'resilience4j-bulkhead:2.3.0' respectively. Keep the AOP dependency as is. After
changes, verify that the build and tests pass successfully.

testImplementation 'org.springframework.security:spring-security-test'
testImplementation 'org.awaitility:awaitility:4.3.0'
testImplementation 'com.h2database:h2'
Expand Down
21 changes: 21 additions & 0 deletions src/main/java/com/example/gtable/global/config/AsyncConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.gtable.global.config;

import java.util.concurrent.Executor;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

@Configuration
public class AsyncConfig {
@Bean(name = "s3UploadExecutor")
public Executor s3UploadExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("S3Upload-");
executor.initialize();
return executor;
}
}
33 changes: 33 additions & 0 deletions src/main/java/com/example/gtable/global/config/AwsS3Config.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example.gtable.global.config;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import com.amazonaws.auth.AWSStaticCredentialsProvider;
import com.amazonaws.auth.BasicAWSCredentials;
import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.AmazonS3ClientBuilder;

@Configuration
public class AwsS3Config {

@Value("${cloud.aws.credentials.access-key}")
private String accessKey;

@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;

@Value("${cloud.aws.region.static}")
private String region;
Comment on lines +15 to +22
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

AWS 크리덴셜 관리 방식을 더 안전하게 개선해보세요.

현재 프로퍼티 파일에서 직접 액세스 키와 시크릿 키를 읽어오는 방식은 보안상 취약할 수 있습니다. 프로덕션 환경에서는 다음 방법들을 고려해보시기 바랍니다:

  1. IAM 역할 기반 인증 사용
  2. AWS Systems Manager Parameter Store 활용
  3. 환경 변수를 통한 크리덴셜 관리

더 안전한 크리덴셜 관리를 위한 개선 방안:

+ // IAM 역할 기반 인증 사용 시
+ @Bean
+ public AmazonS3Client amazonS3ClientWithIAM() {
+     return (AmazonS3Client) AmazonS3ClientBuilder.standard()
+         .withRegion(region)
+         .withCredentials(new DefaultAWSCredentialsProviderChain())
+         .build();
+ }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
@Value("${cloud.aws.region.static}")
private String region;
@Value("${cloud.aws.credentials.access-key}")
private String accessKey;
@Value("${cloud.aws.credentials.secret-key}")
private String secretKey;
@Value("${cloud.aws.region.static}")
private String region;
// IAM 역할 기반 인증 사용 시
@Bean
public AmazonS3Client amazonS3ClientWithIAM() {
return (AmazonS3Client) AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new DefaultAWSCredentialsProviderChain())
.build();
}
🤖 Prompt for AI Agents
In src/main/java/com/example/gtable/global/config/AwsS3Config.java around lines
15 to 22, the current approach reads AWS access and secret keys directly from
properties, which is insecure. Refactor the code to avoid hardcoding credentials
by using IAM roles assigned to the instance or container, or retrieve
credentials securely from AWS Systems Manager Parameter Store or environment
variables. Update the configuration to leverage AWS SDK's default credential
provider chain that automatically picks up credentials from these secure
sources.


@Bean
public AmazonS3Client amazonS3Client() {
BasicAWSCredentials awsCredentials = new BasicAWSCredentials(accessKey, secretKey);
return (AmazonS3Client)AmazonS3ClientBuilder.standard()
.withRegion(region)
.withCredentials(new AWSStaticCredentialsProvider(awsCredentials))
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
.requestMatchers(
"/oauth2/authorization/kakao", // 카카오 로그인 요청
"/login/oauth2/code/**", // 카카오 인증 콜백
"/api/refresh-token") // refresh token (토큰 갱신)
"/api/refresh-token", // refresh token (토큰 갱신)
"/stores/**",
"/menus/**")
.permitAll()
.anyRequest().authenticated() // 그외 요청은 허가된 사람만 인가
)
Expand Down
57 changes: 57 additions & 0 deletions src/main/java/com/example/gtable/global/s3/S3Service.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package com.example.gtable.global.s3;

import java.io.InputStream;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import com.amazonaws.services.s3.AmazonS3Client;
import com.amazonaws.services.s3.model.ObjectMetadata;

import io.github.resilience4j.bulkhead.annotation.Bulkhead;
import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class S3Service {
private final AmazonS3Client amazonS3Client;

@Value("${cloud.aws.s3.bucket}")
private String bucket;

public record S3UploadResult(String key, String url) {
}

@Bulkhead(name = "s3UploadBulkhead", type = Bulkhead.Type.THREADPOOL)
@Async("s3UploadExecutor")
public CompletableFuture<S3UploadResult> upload(String type, Long refId, MultipartFile file) {
try (InputStream inputStream = file.getInputStream()) {
String key = createFileKey(type, refId, file.getOriginalFilename());
ObjectMetadata metadata = new ObjectMetadata();
metadata.setContentLength(file.getSize());

amazonS3Client.putObject(bucket, key, inputStream, metadata);
String url = amazonS3Client.getUrl(bucket, key).toString();

return CompletableFuture.completedFuture(new S3UploadResult(key, url));
} catch (Exception e) {
Comment on lines +31 to +41
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

파일 메타데이터 보강 및 null 파일명 대비 필요

  1. file.getOriginalFilename()null 인 경우 NullPointerException 이 발생해 키 생성에 실패합니다.
  2. Content-Type 을 세팅하지 않아 다운로드 시 MIME 타입이 application/octet-stream 로 표시될 수 있습니다.
-String key = createFileKey(type, refId, file.getOriginalFilename());
+String originalName = file.getOriginalFilename() != null ? file.getOriginalFilename() : "unknown";
+String key = createFileKey(type, refId, originalName);

 ObjectMetadata metadata = new ObjectMetadata();
 metadata.setContentLength(file.getSize());
+metadata.setContentType(file.getContentType());
🤖 Prompt for AI Agents
In src/main/java/com/example/gtable/global/s3/S3Service.java around lines 31 to
41, the code does not handle the case where file.getOriginalFilename() returns
null, which can cause a NullPointerException during key creation. Also, the
ObjectMetadata lacks setting the Content-Type, leading to incorrect MIME type on
download. To fix this, add a null check for the original filename and provide a
default name if null, and set the Content-Type in ObjectMetadata using
file.getContentType() before uploading the file.

throw new RuntimeException("S3 업로드 실패", e);
}
}

public void delete(String filename) {
try {
amazonS3Client.deleteObject(bucket, filename);
} catch (Exception e) {
throw new RuntimeException("S3 파일 삭제 실패", e);
}
}

private String createFileKey(String type, Long refId, String filename) {
return type + "/" + refId + "/" + UUID.randomUUID() + "-" + filename;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.example.gtable.menu.controller;

import org.springframework.http.HttpStatus;
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.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import com.example.gtable.global.api.ApiUtils;
import com.example.gtable.menu.dto.MenuCreateRequest;
import com.example.gtable.menu.dto.MenuCreateResponse;
import com.example.gtable.menu.service.MenuService;

import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;

@RestController
@RequestMapping("/menus")
@RequiredArgsConstructor
public class MenuController {

private final MenuService menuService;

@PostMapping("/create")
public ResponseEntity<?> createMenu(@Valid @RequestBody MenuCreateRequest request) {
MenuCreateResponse response = menuService.createMenu(request);

return ResponseEntity
.status(HttpStatus.CREATED)
.body(
ApiUtils.success(
response
)
);
}

@GetMapping("/{storeId}")
public ResponseEntity<?> getMenusByStoreId(@PathVariable Long storeId) {
return ResponseEntity
.status(HttpStatus.OK)
.body(
ApiUtils.success(
menuService.getMenusByStoreId(storeId)
)
);
}
}
33 changes: 33 additions & 0 deletions src/main/java/com/example/gtable/menu/dto/MenuCreateRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example.gtable.menu.dto;

import com.example.gtable.menu.model.Menu;

import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
public class MenuCreateRequest {

@NotNull
private Long storeId;
@NotNull
private String name;
@NotNull
private String description;
@NotNull
private String price;

public Menu toEntity() {
return Menu.builder()
.storeId(storeId)
.name(name)
.description(description)
.price(price)
.build();
}
}

32 changes: 32 additions & 0 deletions src/main/java/com/example/gtable/menu/dto/MenuCreateResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package com.example.gtable.menu.dto;

import java.time.LocalDateTime;

import com.example.gtable.menu.model.Menu;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@AllArgsConstructor
@Builder
public class MenuCreateResponse {
private Long menuId;
private Long storeId;
private String name;
private String description;
private String price;
private LocalDateTime createdAt;

public static MenuCreateResponse fromEntity(Menu menu) {
return MenuCreateResponse.builder()
.createdAt(menu.getCreatedAt())
.menuId(menu.getId())
.storeId(menu.getStoreId())
.name(menu.getName())
.description(menu.getDescription())
.price(menu.getPrice())
.build();
}
}
33 changes: 33 additions & 0 deletions src/main/java/com/example/gtable/menu/dto/MenuReadDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package com.example.gtable.menu.dto;

import java.util.List;

import com.example.gtable.menu.model.Menu;
import com.example.gtable.menuImage.dto.MenuImageUploadResponse;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@AllArgsConstructor
@Builder
public class MenuReadDto {
private Long menuId;
private Long storeId;
private String name;
private String description;
private String price;
private List<MenuImageUploadResponse> images;

public static MenuReadDto fromEntity(Menu menu, List<MenuImageUploadResponse> images) {
return MenuReadDto.builder()
.menuId(menu.getId())
.storeId(menu.getStoreId())
.name(menu.getName())
.description(menu.getDescription())
.price(menu.getPrice())
.images(images)
.build();
}
}
21 changes: 21 additions & 0 deletions src/main/java/com/example/gtable/menu/dto/MenuReadResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.example.gtable.menu.dto;

import java.util.List;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;

@Getter
@AllArgsConstructor
@Builder
public class MenuReadResponse {

private List<MenuReadDto> menuReadDto;

public static MenuReadResponse of(List<MenuReadDto> menuReadDto) {
return MenuReadResponse.builder()
.menuReadDto(menuReadDto)
.build();
}
}
42 changes: 42 additions & 0 deletions src/main/java/com/example/gtable/menu/model/Menu.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.example.gtable.menu.model;

import java.time.LocalDateTime;

import com.example.gtable.global.entity.BaseTimeEntity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.Table;
import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;

@Entity
@Table(name = "menus")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@AllArgsConstructor(access = AccessLevel.PRIVATE)
@SuperBuilder
public class Menu extends BaseTimeEntity {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long Id;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

필드명 일관성을 위해 수정이 필요합니다

Java 명명 규칙에 따라 필드명은 camelCase를 사용해야 합니다. Idid로 변경해주세요.

-	private Long Id;
+	private Long id;

커스텀 생성자에서도 동일하게 수정이 필요합니다:

-		this.Id = id;
+		this.id = id;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
private Long Id;
private Long id;
this.id = id;
🤖 Prompt for AI Agents
In src/main/java/com/example/gtable/menu/model/Menu.java at line 28, the field
name 'Id' should be renamed to 'id' to follow Java camelCase naming conventions.
Also, update all references to this field, including in the custom constructor,
to use 'id' instead of 'Id' to maintain consistency.

private Long storeId;
private String name;
private String description;
private String price;
Copy link

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

가격 필드의 데이터 타입을 BigDecimal로 변경하는 것을 권장합니다

금액을 다룰 때는 정확한 계산을 위해 String 대신 BigDecimal을 사용하는 것이 좋습니다. 부동소수점 연산의 정밀도 문제를 방지할 수 있습니다.

+import java.math.BigDecimal;

-	private String price;
+	private BigDecimal price;

커스텀 생성자의 매개변수 타입도 함께 변경해주세요:

-	public Menu(LocalDateTime createdAt, Long id, Long storeId, String name, String description, String price) {
+	public Menu(LocalDateTime createdAt, Long id, Long storeId, String name, String description, BigDecimal price) {
🤖 Prompt for AI Agents
In src/main/java/com/example/gtable/menu/model/Menu.java at line 32, change the
data type of the price field from String to BigDecimal to ensure precise
monetary calculations. Also update any custom constructors that accept price as
a parameter to use BigDecimal instead of String, maintaining consistency across
the class.


public Menu(LocalDateTime createdAt, Long id, Long storeId, String name, String description, String price) {
super(createdAt);
this.Id = id;
this.storeId = storeId;
this.name = name;
this.description = description;
this.price = price;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.gtable.menu.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import com.example.gtable.menu.model.Menu;

@Repository
public interface MenuRepository extends JpaRepository<Menu, Long> {
List<Menu> findAllByStoreId(Long storeId);
}
Loading
Loading