Skip to content

Commit 04a5133

Browse files
committed
part2-김경린-sprint7
1 parent 517ee44 commit 04a5133

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+5017
-388
lines changed

.github/img_4.png

138 KB
Loading

.github/pull-request-template.md

+179-223
Large diffs are not rendered by default.

.logs/app.2025-03-28.log

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
25-03-28 14:34:43.799 [main] INFO c.s.m.discodeit.DiscodeitApplication - Starting DiscodeitApplication using Java 17.0.12 with PID 70960 (C:\Users\User\OneDrive\Desktop\codeit\1-sprint-mission\build\classes\java\main started by User in C:\Users\User\OneDrive\Desktop\codeit\1-sprint-mission)
2+
25-03-28 14:34:43.801 [main] INFO c.s.m.discodeit.DiscodeitApplication - The following 1 profile is active: "prod"
3+
25-03-28 14:34:46.614 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Bootstrapping Spring Data JPA repositories in DEFAULT mode.
4+
25-03-28 14:34:46.798 [main] INFO o.s.d.r.c.RepositoryConfigurationDelegate - Finished Spring Data repository scanning in 169 ms. Found 6 JPA repository interfaces.
5+
25-03-28 14:34:48.014 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat initialized with port 8080 (http)
6+
25-03-28 14:34:48.036 [main] INFO o.a.coyote.http11.Http11NioProtocol - Initializing ProtocolHandler ["http-nio-8080"]
7+
25-03-28 14:34:48.041 [main] INFO o.a.catalina.core.StandardService - Starting service [Tomcat]
8+
25-03-28 14:34:48.041 [main] INFO o.a.catalina.core.StandardEngine - Starting Servlet engine: [Apache Tomcat/10.1.33]
9+
25-03-28 14:34:48.201 [main] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring embedded WebApplicationContext
10+
25-03-28 14:34:48.202 [main] INFO o.s.b.w.s.c.ServletWebServerApplicationContext - Root WebApplicationContext: initialization completed in 4324 ms
11+
25-03-28 14:34:48.448 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Starting...
12+
25-03-28 14:34:48.946 [main] INFO com.zaxxer.hikari.pool.HikariPool - HikariPool-1 - Added connection org.postgresql.jdbc.PgConnection@2fdf22f7
13+
25-03-28 14:34:48.950 [main] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Start completed.
14+
25-03-28 14:34:49.192 [main] INFO o.h.jpa.internal.util.LogHelper - HHH000204: Processing PersistenceUnitInfo [name: default]
15+
25-03-28 14:34:49.360 [main] INFO org.hibernate.Version - HHH000412: Hibernate ORM core version 6.5.3.Final
16+
25-03-28 14:34:49.449 [main] INFO o.h.c.i.RegionFactoryInitiator - HHH000026: Second-level cache disabled
17+
25-03-28 14:34:50.179 [main] INFO o.s.o.j.p.SpringPersistenceUnitInfo - No LoadTimeWeaver setup: ignoring JPA class transformer
18+
25-03-28 14:34:52.324 [main] INFO o.h.e.t.j.p.i.JtaPlatformInitiator - HHH000489: No JTA platform available (set 'hibernate.transaction.jta.platform' to enable JTA platform integration)
19+
25-03-28 14:34:52.476 [main] INFO o.s.o.j.LocalContainerEntityManagerFactoryBean - Initialized JPA EntityManagerFactory for persistence unit 'default'
20+
25-03-28 14:34:53.625 [main] WARN o.s.b.a.o.j.JpaBaseConfiguration$JpaWebConfiguration - spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning
21+
25-03-28 14:34:53.656 [main] INFO o.s.b.a.w.s.WelcomePageHandlerMapping - Adding welcome page: class path resource [static/index.html]
22+
25-03-28 14:34:55.632 [main] INFO o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
23+
25-03-28 14:34:55.678 [main] INFO o.s.b.w.e.tomcat.TomcatWebServer - Tomcat started on port 8080 (http) with context path '/'
24+
25-03-28 14:34:55.697 [main] INFO c.s.m.discodeit.DiscodeitApplication - Started DiscodeitApplication in 12.781 seconds (process running for 21.381)
25+
25-03-28 14:35:23.964 [http-nio-8080-exec-1] INFO o.a.c.c.C.[Tomcat].[localhost].[/] - Initializing Spring DispatcherServlet 'dispatcherServlet'
26+
25-03-28 14:35:23.964 [http-nio-8080-exec-1] INFO o.s.web.servlet.DispatcherServlet - Initializing Servlet 'dispatcherServlet'
27+
25-03-28 14:35:23.967 [http-nio-8080-exec-1] INFO o.s.web.servlet.DispatcherServlet - Completed initialization in 2 ms
28+
25-03-28 14:35:26.004 [http-nio-8080-exec-2] INFO o.s.api.AbstractOpenApiResource - Init duration for springdoc-openapi is: 1115 ms
29+
25-03-28 14:44:14.631 [SpringApplicationShutdownHook] INFO o.s.o.j.LocalContainerEntityManagerFactoryBean - Closing JPA EntityManagerFactory for persistence unit 'default'
30+
25-03-28 14:44:14.633 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown initiated...
31+
25-03-28 14:44:14.642 [SpringApplicationShutdownHook] INFO com.zaxxer.hikari.HikariDataSource - HikariPool-1 - Shutdown completed.

.logs/app.2025-03-30.log

+268
Large diffs are not rendered by default.

.logs/app.2025-03-31.log

+225
Large diffs are not rendered by default.

.logs/app.2025-04-01.log

+2,383
Large diffs are not rendered by default.

.logs/app.log

+1,429
Large diffs are not rendered by default.

error-log/DB연결 오류.md

+77
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,77 @@
1+
문제 상황 :
2+
3+
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean
4+
with name 'dataSourceScriptDatabaseInitializer' defined in class path
5+
resource [org/springframework/boot/autoconfigure/sql/init/DataSourceInitializationConfiguration.class]:
6+
Unsatisfied dependency expressed through method 'dataSourceScriptDatabaseInitializer' parameter 0:
7+
Error creating bean with name 'dataSource' defined in class path
8+
resource [org/springframework/boot/autoconfigure/jdbc/DataSourceConfiguration$Hikari.class]: Failed
9+
to instantiate [com.zaxxer.hikari.HikariDataSource]: Factory method 'dataSource' threw exception
10+
with message: Failed to load driver class org.h2.Driver in either of HikariConfig class loader or
11+
Thread context classloader
12+
13+
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean
14+
with name 'dataSourceScriptDatabaseInitializer' defined in class path
15+
16+
클래스 경로에 'dataSourceScriptDatabaseInitializer'라는 이름으로 정의된 빈을 생성할 수 없는 오류가 발생했다.
17+
UnsatisfiedDependencyException = 불만족스러운 종속성 예외
18+
19+
Failed to load driver class org.h2.Driver in either of HikariConfig class loader or
20+
Thread context classloader
21+
H2 드라이버(org.h2.Driver)를 로드할 수 없다.
22+
23+
해결 : 의존성 추가, 설정 파일에 설정 추가
24+
25+
1. 의존성 추가
26+
![img_1.png](img_1.png)
27+
28+
새로운 의존성을 추가한 후 다운로드할 수 있게 build.gradle 을 통해 org.h2.Driver 클래스가 인식되도록 했다.
29+
30+
2. yml 설정 파일에 설정 추가
31+
![img_2.png](img_2.png)
32+
33+
공통 설정 파일에 spring:profiles:active: dev, datasource:driver-class-name: org.h2.Driver 를 설정했다.
34+
35+
![img_3.png](img_3.png)
36+
37+
38+
-----
39+
40+
문제 상황2 :
41+
42+
2025-03-27T16:14:02.827+09:00 ERROR 18156 --- [discodeit] [ main]
43+
j.LocalContainerEntityManagerFactoryBean : Failed to initialize JPA
44+
EntityManagerFactory: [PersistenceUnit: default] Unable to build Hibernate SessionFactory; nested
45+
exception is org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing
46+
table [message]
47+
2025-03-27T16:14:02.830+09:00 WARN 18156 --- [discodeit] [ main]
48+
ConfigServletWebServerApplicationContext : Exception encountered during context initialization -
49+
cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating
50+
bean with name 'entityManagerFactory' defined in class path
51+
resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaConfiguration.class]: [PersistenceUnit: default]
52+
Unable to build Hibernate SessionFactory; nested exception is
53+
org.hibernate.tool.schema.spi.SchemaManagementException: Schema-validation: missing table [message]
54+
55+
Schema-validation: missing table [message]
56+
스키마 검증 단계에서 message 라는 테이블이 없어서 발생한 문제이다.
57+
58+
해결 : H2 DB에 테이블 생성
59+
60+
DB에 message 테이블이 없거나, 아예 테이블을 생성하는 SQL(schema.sql)이 실행되지 않았거나 둘 중 하나라고 생각했다.
61+
때문에 H2 DB에 데이터테이블을 생성하는 방법을 찾자, 두 가지 방법이 나왔다.
62+
63+
1. 스프링 부트의 DB 테이블 만들기
64+
- spring.sql.init.mode 사용
65+
2. 하이버네이트가 테이블을 자동 생성해주기
66+
- jpa:hibernate:ddl-auto : 를 create, update 로 지정
67+
68+
이때, 이 두가지 설정을 동시에 이용하면 안 된다. 스프링 부트에서 테이블을 생성하려고 하는 동작과 하이버네이트가 자동 테이블을 자동 생성하려는 동작에 충돌이 있다.
69+
![img_6.png](img_6.png)
70+
실제로 jpa:hibernate:ddl-auto를 validation으로 지정한 후 테이블을 생성하려고 하자, 여전히 message 테이블이 존재하지 않는다는 오류가 동일하게
71+
떴다.
72+
이후 jpa:hibernate:ddl-auto를 none으로 설정하자, 애플리케이션이 실행될 수 있었다.
73+
74+
[[Spring Boot] DB Schema 및 Data 초기화 schema.sql data.sql
75+
](https://chaewsscode.tistory.com/174)
76+
[[Spring] org.springframework.beans.factory.UnsatisfiedDependencyException 에러](https://yn98.tistory.com/84#%EC%A3%BC%EC%9A%94%20%EC%9B%90%EC%9D%B8-1)
77+
[SQL 데이터베이스](https://docs.spring.io/spring-boot/reference/data/sql.html#data.sql.h2-web-console)

error-log/img_1.png

39.2 KB
Loading

error-log/img_2.png

16.3 KB
Loading

error-log/img_3.png

107 KB
Loading

error-log/img_6.png

48.6 KB
Loading

error-log/img_7.png

274 KB
Loading
+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
### 문제 상황 :
2+
3+
Strict stubbing argument mismatch. Please check:
4+
5+
- this invocation of 'toDto' method:
6+
userMapper.toDto(
7+
com.sprint.mission.discodeit.entity.User@743c6ce4
8+
);
9+
-> at com.sprint.mission.discodeit.service.basic.BasicUserService.createUser(
10+
BasicUserService.java:92)
11+
- has following stubbing(s) with different arguments:
12+
1. userMapper.toDto(
13+
com.sprint.mission.discodeit.entity.User@30669dac
14+
);
15+
-> at com.sprint.mission.discodeit.service.UserServiceTest.createUser_Success(
16+
UserServiceTest.java:77)
17+
Typically, stubbing argument mismatch indicates user mistake when writing tests.
18+
Mockito fails early so that you can debug potential problem easily.
19+
However, there are legit scenarios when this exception generates false negative signal:
20+
- stubbing the same method multiple times using 'given().will()' or 'when().then()' API
21+
Please use 'will().given()' or 'doReturn().when()' API for stubbing.
22+
- stubbed method is intentionally invoked with different arguments by code under test
23+
Please use default or 'silent' JUnit Rule (equivalent of Strictness.LENIENT).
24+
For more information see javadoc for PotentialStubbingProblem class.
25+
org.mockito.exceptions.misusing.PotentialStubbingProblem:
26+
Strict stubbing argument mismatch. Please check:
27+
- this invocation of 'toDto' method:
28+
userMapper.toDto(
29+
com.sprint.mission.discodeit.entity.User@743c6ce4
30+
);
31+
32+
### 문제가 일어난 이유 : User 객체가 테스트 코드에서 예상한 것과 다른 인수로 호출됐다.
33+
34+
Strict stubbing argument mismatch. Please check:
35+
Strict stubbing argument mismatch. Please check:
36+
37+
- this invocation of 'toDto' method:
38+
userMapper.toDto(
39+
com.sprint.mission.discodeit.entity.User@743c6ce4
40+
);
41+
-> at com.sprint.mission.discodeit.service.basic.BasicUserService.createUser(
42+
BasicUserService.java:92)
43+
- has following stubbing(s) with different arguments:
44+
1. userMapper.toDto(
45+
com.sprint.mission.discodeit.entity.User@30669dac
46+
);
47+
-> at com.sprint.mission.discodeit.service.UserServiceTest.createUser_Success(
48+
UserServiceTest.java:77)
49+
50+
실제로 호출된 userMapper.toDto() 메서드는 com.sprint.mission.discodeit.entity.User@743c6ce4 객체를 인자로 받았지만,
51+
테스트에서 stubbing한 호출은 com.sprint.mission.discodeit.entity.User@30669dac 객체를 인자로 기대했습니다.
52+
53+
테스트에서 생성된 user 객체와 BasicUserService.createUser() 메서드 내부에서 생성되는 User 객체가 서로 다른 인스턴스이기 때문에 발생한 문제입니다.
54+
55+
### 해결 : user 객체가 expected 값과 동일한 값으로 stub될 수 있도록 설정해야 한다.
56+
57+
user 을 전달받던 userMapper.toDto() 메서드에 `any(User.class)` 를 전달한다.
58+
테스트 의의에 맞게 변경했다.
+11
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
### 상황 :
2+
3+
![img_7.png](img_7.png)
4+
jakarta.validation.UnexpectedTypeException: HV000030: 'jakarta.validation.constraints.NotBlank' 제약
5+
조건에 대한 유효성 검사기를 찾을 수 없습니다. 'java.util.UUID' 유형을 유효성 검사합니다. 'authorId'에 대한 구성을 확인합니다
6+
7+
### 문제가 일어난 이유 : @NotBlank 제약 조건은 문자열(String) 유형에 적용해야 한다.
8+
9+
UUID 유형에 @NotBlank 제약 조건을 걸어놔 발생한 문제였다.
10+
11+
### 해결 : @NotBlank -> @NotNull

src/main/java/com/sprint/mission/discodeit/config/AppConfig.java

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
public class AppConfig {
1111

1212
@Bean
13-
@ConditionalOnProperty(name = "discodeit.stroage.type", havingValue = "local")
13+
@ConditionalOnProperty(name = "discodeit.storage.type", havingValue = "local")
1414
public BinaryContentStorage binaryContentStorage() {
1515
return new LocalBinaryContentStorage();
1616
}

src/main/java/com/sprint/mission/discodeit/controller/AuthController.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package com.sprint.mission.discodeit.controller;
22

3+
import com.sprint.mission.discodeit.controller.api.AuthApi;
34
import com.sprint.mission.discodeit.dto.user.UserLoginRequest;
45
import com.sprint.mission.discodeit.entity.User;
56
import com.sprint.mission.discodeit.service.basic.AuthService;
@@ -13,7 +14,7 @@
1314
@RestController
1415
@RequestMapping("/api/auth")
1516
@RequiredArgsConstructor
16-
public class AuthController {
17+
public class AuthController implements AuthApi {
1718

1819
private final AuthService authService;
1920

src/main/java/com/sprint/mission/discodeit/controller/BinaryFileController.java

+1-5
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,7 @@ public ResponseEntity<BinaryContentDto> binaryContentFindById(
3030
@GetMapping(value = "/{binaryContentId}/download")
3131
public ResponseEntity<?> downloadBinaryContent(@PathVariable UUID id) {
3232
log.info("파일 다운로드 요청(Request)");
33-
// BinaryContentStorage 를 직접 들고와서 쓰라는 것(클래스 다이어그램)으로 이해는 했지만,
34-
// 그러면 컨트롤러 단에서 Mapper를 통한 변환(id로 BinaryContent 를 부르고, BinaryContent <-> DTO)이 이뤄이지 때문에
35-
// 저는 우선 binaryContentService 에 download 관련 메서드를 추가했습니다.
36-
// 내부적으로는 binaryContentStorage 의 downlaod 메서드가 호출됩니다.
37-
// 즉, 다운로드 API -> (컨트롤러 - 서비스 - 스토리지) 단에서 기능 구현
33+
3834
ResponseEntity<?> downloadBinaryContent = binaryContentService.downloadBinaryContent(id);
3935
log.info("파일 다운로드 응답(Response): HttpStatus={}", HttpStatus.OK);
4036
return downloadBinaryContent;

src/main/java/com/sprint/mission/discodeit/controller/ChannelController.java

+2-9
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.sprint.mission.discodeit.dto.ChannelDto;
44
import com.sprint.mission.discodeit.dto.channel.*;
5-
import com.sprint.mission.discodeit.entity.Channel;
65
import com.sprint.mission.discodeit.service.ChannelService;
76
import jakarta.validation.Valid;
87
import lombok.RequiredArgsConstructor;
@@ -45,13 +44,8 @@ public ResponseEntity<ChannelDto> createPrivateChannel(
4544
}
4645

4746

48-
// 스프린트 미션 5 심화 조건 중 API 스펙을 준수를 위해 변경했지만,
49-
// /public/{channelId}, /private/{channelId} 처럼 구분하는 건 어떨까요?
50-
// 제가 느끼기에는 확장성은 조금 부족할지라도, 엔드포인트를 통해 public 채널인지 private 채널인지 확실히 구분할 수 있어 보여 좋아보이는데
51-
// 위와 같은 엔드 포인트를 사용했을 때의 단점도 궁금합니다!(public, prviate 뿐만 아니라 type 더 추가되면 더 복잡해진다거나)
52-
// 무엇보다 Public 채널을 업데이트하는 건데 엔드 포인트에 public이 붙지 않는 것도 고민되는 부분입니다.
5347
@PatchMapping(value = "/{channelId}")
54-
public ResponseEntity<ChannelDto> updatePublicChannel(@PathVariable UUID channelId,
48+
public ResponseEntity<ChannelDto> updatePublicChannel(@PathVariable("channelId") UUID channelId,
5549
@Valid @RequestBody ChannelUpdateRequest channelUpdateRequest) {
5650
log.info("채널 수정 요청(Request): nameChanged={}, descriptionChanged={}",
5751
channelUpdateRequest.newName() != null,
@@ -62,10 +56,9 @@ public ResponseEntity<ChannelDto> updatePublicChannel(@PathVariable UUID channel
6256
return ResponseEntity.ok(channelService.updateChannel(channelId, channelUpdateRequest));
6357
}
6458

65-
// 궁금한게, Public 과 Private 채널을 만들 땐 /public, /private 엔드포인트로 들어오는데 삭제할 때는 id만 해놔도 되는지
6659
@DeleteMapping(value = "/{channelId}")
6760
public ResponseEntity<Void> deleteChannel(
68-
@PathVariable UUID channelId) {
61+
@PathVariable("channelId") UUID channelId) {
6962
log.info("채널 삭제 요청(Request)");
7063
channelService.deleteChannelById(channelId);
7164
log.info("채널 삭제 응답(Response): HttpStatus={}", HttpStatus.NO_CONTENT);

src/main/java/com/sprint/mission/discodeit/controller/MessageController.java

+19-10
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,13 @@
66

77
import com.sprint.mission.discodeit.dto.message.MessageUpdateRequest;
88
import com.sprint.mission.discodeit.dto.reponse.PageResponse;
9-
import com.sprint.mission.discodeit.entity.Message;
109
import com.sprint.mission.discodeit.service.MessageService;
1110
import jakarta.validation.Valid;
11+
import java.io.IOException;
1212
import java.util.ArrayList;
13+
import java.util.Optional;
1314
import lombok.RequiredArgsConstructor;
1415
import lombok.extern.slf4j.Slf4j;
15-
import org.springframework.data.domain.Page;
1616
import org.springframework.data.domain.PageRequest;
1717
import org.springframework.data.domain.Pageable;
1818
import org.springframework.data.domain.Sort;
@@ -43,17 +43,26 @@ public ResponseEntity<MessageDto> createMessage(
4343
attachments != null);
4444

4545
// 메세지 첨부 파일 생성
46-
List<BinaryContentCreateRequest> binaryContentCreateRequests = new ArrayList<>();
47-
if (attachments != null) {
48-
for (MultipartFile file : attachments) {
49-
log.debug("메세지 첨부 파일 생성: fileName={}", file.getName());
50-
binaryContentCreateRequests.add(new BinaryContentCreateRequest(file));
51-
}
52-
}
46+
List<BinaryContentCreateRequest> attachmentRequests = Optional.ofNullable(attachments)
47+
.map(files -> files.stream()
48+
.map(file -> {
49+
try {
50+
return new BinaryContentCreateRequest(
51+
file.getOriginalFilename(),
52+
file.getSize(),
53+
file.getContentType(),
54+
file.getBytes()
55+
);
56+
} catch (IOException e) {
57+
throw new RuntimeException(e);
58+
}
59+
})
60+
.toList())
61+
.orElse(new ArrayList<>());
5362

5463
// 메세지 생성
5564
MessageDto messageDto = messageService.createMessage(messageCreateRequest,
56-
binaryContentCreateRequests);
65+
attachmentRequests);
5766

5867
log.info("메세지 생성 응답(Response): messageContent={}, HttpStatus={}",
5968
messageDto.content(),

0 commit comments

Comments
 (0)