Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import java.util.UUID;
import java.util.stream.Collectors;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

Expand All @@ -18,29 +17,20 @@
import com.newzet.api.article.domain.Article;
import com.newzet.api.article.exception.ShareForbiddenException;
import com.newzet.api.article.repository.dto.ArticleWithImageProjection;
import com.newzet.api.common.s3.S3Service;
import com.newzet.api.common.util.UuidConverter;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class ArticleService {

private final static String ARTICLE_SHARE_PREFIX = "https://app.newzet.me/article";
private final ArticleBatchProducer batchProducer;
private final ArticleRepository articleRepository;
private final S3Service s3Service;
private final String contentBucketName;

public ArticleService(ArticleBatchProducer batchProducer, ArticleRepository articleRepository,
S3Service s3Service, @Value("s3.content-bucket") String contentBucketName) {
this.batchProducer = batchProducer;
this.articleRepository = articleRepository;
this.s3Service = s3Service;
this.contentBucketName = contentBucketName;
}

public void saveArticleBatch(UUID userId, String fromName, String fromDomain,
String mailingList, String imageUrl, String htmlLink, String title) {
Expand Down Expand Up @@ -132,10 +122,4 @@ public Article getSharedArticle(UUID articleId) {
}
throw new ShareForbiddenException("공유가 허용되지 않은 아티클입니다.");
}

public String getContentUrl() {
//TODO(S3에서 Article 조회)
//TODO(getArticle도 바꿔주야함)
return "";
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import com.newzet.api.article.controller.dto.ArticleContentResponse;
import com.newzet.api.article.domain.Article;
import com.newzet.api.common.s3.S3Service;
import com.newzet.api.config.s3.S3Config;

import lombok.RequiredArgsConstructor;

Expand All @@ -20,26 +19,18 @@ public class ArticleOrchestrator {

private final ArticleService articleService;
private final S3Service s3Service;
private final S3Config s3Config;

public ArticleContentResponse getSharedArticle(UUID articleId) {
Article sharedArticle = articleService.getSharedArticle(articleId);
String articleContent = articleService.getContentUrl();
return new ArticleContentResponse(sharedArticle.getTitle(), articleContent,
String content = s3Service.getContentAsString(sharedArticle.getContentUrl());
return new ArticleContentResponse(sharedArticle.getTitle(), content,
sharedArticle.isLike());
}

public ArticleContentResponse getArticle(UUID articleId) {
Article article = articleService.getArticle(articleId);
// S3로부터 본문 HTML를 받아옴
// contentUrl.html 형태로 접근해서 가져와, content 필드에 주입
String content = getContentFromS3(article.getContentUrl());

String content = s3Service.getContentAsString(article.getContentUrl());
return ArticleContentResponse.of(article.getTitle(), content,
article.isLike());
}

private String getContentFromS3(String key) {
return s3Service.getContentAsString(s3Config.getContentBucketName(), key);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@

@Getter
@RequiredArgsConstructor
//TODO: 코드 잘못 던지고 있는 부분 수정
public enum ResponseCode {
SUCCESS(20000),
INVALID_ARGUMENTS(40000),
Expand Down
6 changes: 4 additions & 2 deletions src/main/java/com/newzet/api/common/s3/S3Service.java
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.newzet.api.common.exception.InternalErrorException;
import com.newzet.api.config.s3.S3Config;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -20,9 +21,10 @@
@Slf4j
public class S3Service {
private final AmazonS3 amazonS3;
private final S3Config s3Config;

public String getContentAsString(String bucketName, String key) {
GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, key);
public String getContentAsString(String key) {
GetObjectRequest getObjectRequest = new GetObjectRequest(s3Config.getContentBucketName(), key);

try (S3Object s3Object = amazonS3.getObject(getObjectRequest);
S3ObjectInputStream inputStream = s3Object.getObjectContent()) {
Expand Down
17 changes: 10 additions & 7 deletions src/test/java/com/newzet/api/common/s3/S3ServiceTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.amazonaws.services.s3.model.S3Object;
import com.amazonaws.services.s3.model.S3ObjectInputStream;
import com.newzet.api.common.exception.InternalErrorException;
import com.newzet.api.config.s3.S3Config;

@ExtendWith(MockitoExtension.class)
class S3ServiceTest {
Expand All @@ -31,6 +32,8 @@ class S3ServiceTest {
private S3Service s3Service;
@Mock
private AmazonS3 amazonS3; // S3Service가 사용하는 SDK v1 클라이언트
@Mock
private S3Config s3Config;

@Nested
@DisplayName("성공 케이스")
Expand All @@ -42,16 +45,16 @@ void getContentAsString_Success() {
String expectedContent = "S3 Object Content";
byte[] contentBytes = expectedContent.getBytes(StandardCharsets.UTF_8);

// S3Object와 그 내용을 Mocking
S3Object mockS3Object = new S3Object();
S3ObjectInputStream inputStream = new S3ObjectInputStream(
new ByteArrayInputStream(contentBytes), null);
mockS3Object.setObjectContent(inputStream);

when(s3Config.getContentBucketName()).thenReturn(BUCKET_NAME);
when(amazonS3.getObject(any(GetObjectRequest.class))).thenReturn(mockS3Object);

// when
String actualContent = s3Service.getContentAsString(BUCKET_NAME, OBJECT_KEY);
String actualContent = s3Service.getContentAsString(OBJECT_KEY);

// then
assertEquals(expectedContent, actualContent);
Expand All @@ -65,33 +68,33 @@ class FailureCases {
@DisplayName("S3에 해당 Key의 객체가 없으면 InternalErrorException을 던진다")
void getContentAsString_ThrowsNoSuchKeyException() {
// given
when(s3Config.getContentBucketName()).thenReturn(BUCKET_NAME);
when(amazonS3.getObject(any(GetObjectRequest.class)))
.thenThrow(new AmazonS3Exception("The specified key does not exist."));

// when & then
assertThrows(InternalErrorException.class, () -> {
s3Service.getContentAsString(BUCKET_NAME, OBJECT_KEY);
s3Service.getContentAsString(OBJECT_KEY);
});
}

@Test
@DisplayName("스트림을 읽는 중 IOException이 발생하면 InternalErrorException을 던진다")
void getContentAsString_ThrowsIOException() throws IOException {
// given
// S3Object와 InputStream을 각각 Mocking하여 InputStream의 동작을 제어
S3Object mockS3Object = mock(S3Object.class);
S3ObjectInputStream mockInputStream = mock(S3ObjectInputStream.class);

when(s3Config.getContentBucketName()).thenReturn(BUCKET_NAME);
when(amazonS3.getObject(any(GetObjectRequest.class))).thenReturn(mockS3Object);
when(mockS3Object.getObjectContent()).thenReturn(mockInputStream);

// 핵심: InputStream에서 readAllBytes() 호출 시 강제로 IOException 발생
when(mockInputStream.readAllBytes()).thenThrow(new IOException("스트림 읽기 실패"));

// when & then
assertThrows(InternalErrorException.class, () -> {
s3Service.getContentAsString(BUCKET_NAME, OBJECT_KEY);
s3Service.getContentAsString(OBJECT_KEY);
});
}
}
}
}
Loading