public void validateDuplicateMember(Member member) {
List<Member> findMembers = memberRepository.findByName(member.getName());
if(!findMembers.isEmpty()){
throw new IllegalStateException("이미 존재하는 회원입니다.");
}
}
그대로 코드를 사용할 수는 있지만, IllegalStateException 대신 CustomException을 사용해보고 싶었습니다.
왜냐면 HttpStatus도 함께 사용하는 멋진 코드를 발견했기 때문이죠.
그렇기에 이 글을
Error Code를 Enum 형태로 만들고
CustomException을 만들어
Test Code를 작성해 본다
를 정리한 글입니다.
1. Error code Enum 생성
package com.msgs.msgs.error;
import lombok.Getter;
import org.springframework.http.HttpStatus;
@Getter
public enum ErrorCode {
// 회원
CHECK_LOGIN_ID_OR_PASSWORD(HttpStatus.NOT_FOUND, "아이디 또는 비밀번호를 확인해주세요."),
DUPLICATED_EMAIL(HttpStatus.BAD_REQUEST, "이미 존재하는 이메일 입니다.");
private final HttpStatus httpStatus;
private final String message;
ErrorCode(HttpStatus httpStatus, String message) {
this.httpStatus = httpStatus;
this.message = message;
}
}
제가 알던 Enum과는 모양이 달라서 많이 놀랐습니다.
Enum은 아래와 같이 간단한 열거형만 써봤기 때문이죠.
public enum ErrorCode {
CHECK_LOGIN_ID_OR_PASSWORD,
DUPLICATED_EMAIL,
}
Enum 요소에 특정 값을 연결하려면 필드값을 추가해주면 됩니다.
Enum과 필드값을 매핑해주면, if문의 사용을 줄여 가독성을 높일 수 있습니다.
이 때, 유의해야 할 점은 반드시 Enum 값 선언 이후에 이후에 필드/생성자가 위치해야 한다는 것입니다.
그리고 Enum class에서 자체적으로 name()를 제공하기 때문에, 필드값을 name을 사용하지 않는 것이 좋습니다.
+ Enum은 Enum 값으로 데이터를 찾을 수 있지만 필드의 경우에는 따로 메서드를 만들어 줘야 합니다.
// HttpStatus -> ErrorCode 조회
private static final Map<HttpStatus, ErrorCode> BY_HTTPSTATUS =
Stream.of(values()).collect(Collectors.toMap(ErrorCode::getHttpStatus, e -> e));
public static Optional<ErrorCode> valueOfHttpStatus(HttpStatus httpStatus){
return Optional.ofNullable(BY_HTTPSTATUS.get(httpStatus));
}
// Message -> ErrorCode 조회
private static final Map<String, ErrorCode> BY_MESSAGE =
Stream.of(values()).collect(Collectors.toMap(ErrorCode::getMessage, e -> e));
public static Optional<ErrorCode> valueOfMessage(String message){
return Optional.ofNullable(BY_MESSAGE.get(message));
}
2. CustomException 생성
ErrorCode를 받아주는 CustomException을 만들어줍니다.
@Getter
public class BusinessException extends RuntimeException{
private ErrorCode errorCode;
public BusinessException(ErrorCode errorCode) {
this.errorCode = errorCode;
}
}
RuntimeException도 상속을 받습니다.
3. Test Code 작성
1 기능, 1 테스트 코스..
특히 테스트 코드는 익숙하지 않기 때문에 기능에 맞춰 계속 추가해 볼 예정입니다.
@SpringBootTest
@Transactional
public class UserServiceTest {
// ...
@Test
@DisplayName("이메일이 동일한 회원이 존재할 경우, 예외가 발생한다.")
public void emailDuplicateCheck() throws Exception {
// given
User userA = new User();
userA.setStatus("M");
userA.setEmail("test@email.com");
userA.setPhone("01023456789");
userService.create(userA);
String existingEmail = "test@email.com";
// when
// then
BusinessException exception = assertThrows(BusinessException.class,
() -> userService.emailDuplicateCheck(existingEmail));
assertEquals(DUPLICATED_EMAIL, exception.getErrorCode());
}
}
🙋♀️
본 포스트는 공부 목적으로 작성하였습니다. 보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.
Test Code가 없던 1년 전 마실가실, 리팩토링을 하면서 Test Code가 생겼습니다.
예상 동작과 실제 동작을 비교하여 빠르고 정확한 테스트를 위해서 Test Code를 작성한다고 하는데,
개인적으로 가장 큰 장점은 독립적인 테스트가 가능하다는 것이라고 생각합니다.
저의 리팩토링은 Front가 아닌 Back이고, 따라서 바인딩은 수정이 안된 상황입니다.
화면에서는 정확하게 나오지 않을 확률이 99%입니다.
바인딩까지 수정하면 너무 긴 시간을 기다리게 됩니다.
그렇다고, System.out.println()을 하나하나 확인하기에는 정확성이 떨어질 수 있습니다.
또한, 다른 기능이 아직 리팩토링되지 않아 동작을 하지 않을 경우 새로 고친 코드가 맞는지는 이런 식으로 하다가는 프로그램이 완성되어야 알 수 있게 됩니다.
이에 대한 대안이 Test Code라고 생각합니다.
기존에 설정조차 안되어 있었기 때문에
환경 설정을 하고
테스트 코드를 작성해서
오류를 수정할 예정입니다.
1. 환경 설정
build.gradle
// 추가
dependencies {
testImplementation 'org.springframework.boot:spring-boot-starter-test'
}
// 추가
test {
useJUnitPlatform()
}
// test 태스크가 정의될 때 설정을 직접 적용
// 삭제
task.named('test'){
useJUnitPlatform()
}
// 동적으로 태스크를 참조하거나 태스크가 조건부로 생성되는 경우 사용
2. Test Code 작성
UserServiceTest.java
@SpringBootTest
@Transactional
public class UserServiceTest {
@Autowired
UserService userService;
@Autowired
UserRepository userRepository;
@Test
public void 회원가입() throws Exception {
// given
User user = new User();
user.setStatus("M");
user.setEmail("test@email.com");
user.setPhone("010-2345-6789");
// when
Integer createdId = userService.create(user);
// then
assertThat(user).isEqualTo(userRepository.findOne(createdId));
}
}
@EnableJpaAuditing, @EntityListeners(AuditingEntityListener.class) 추가
3. 오류 수정
사실 자잘한 오류는 문제가 아니었습니다.
테스트 자체가 안돌아갔던 큰 문제가 있었기 때문이죠.
contextLoads()
java.lang.IllegalStateException: Failed to load ApplicationContext for [WebMergedContextConfiguration@2caa5d7c testClass = com.msgs.main.MsgsApplicationTests, locations = [], classes = [com.msgs.main.MsgsApplication], contextInitializerClasses = [], activeProfiles = [], propertySourceLocations = [], propertySourceProperties = ["org.springframework.boot.test.context.SpringBootTestContextBootstrapper=true"], contextCustomizers = [org.springframework.boot.test.autoconfigure.actuate.observability.ObservabilityContextCustomizerFactory$DisableObservabilityContextCustomizer@1f, org.springframework.boot.test.autoconfigure.properties.PropertyMappingContextCustomizer@0, org.springframework.boot.test.autoconfigure.web.servlet.WebDriverContextCustomizerFactory$Customizer@3cc41abc, org.springframework.boot.test.context.filter.ExcludeFilterContextCustomizer@6f2cfcc2, org.springframework.boot.test.json.DuplicateJsonObjectContextCustomizerFactory$DuplicateJsonObjectContextCustomizer@ec2cc4, org.springframework.boot.test.mock.mockito.MockitoContextCustomizer@0, org.springframework.boot.test.web.client.TestRestTemplateContextCustomizer@2f4205be, org.springframework.boot.test.web.reactive.server.WebTestClientContextCustomizer@544820b7, org.springframework.boot.test.context.SpringBootTestAnnotation@d3394ad], resourceBasePath = "src/main/webapp", contextLoader = org.springframework.boot.test.context.SpringBootContextLoader, parent = null]
at org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:142)
at org.springframework.test.context.support.DefaultTestContext.getApplicationContext(DefaultTestContext.java:127)
at org.springframework.test.context.web.ServletTestExecutionListener.setUpRequestContextIfNecessary(ServletTestExecutionListener.java:191)
at org.springframework.test.context.web.ServletTestExecutionListener.prepareTestInstance(ServletTestExecutionListener.java:130)
at org.springframework.test.context.TestContextManager.prepareTestInstance(TestContextManager.java:241)
at org.springframework.test.context.junit.jupiter.SpringExtension.postProcessTestInstance(SpringExtension.java:138)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$10(ClassBasedTestDescriptor.java:377)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.executeAndMaskThrowable(ClassBasedTestDescriptor.java:382)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$invokeTestInstancePostProcessors$11(ClassBasedTestDescriptor.java:377)
at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
at java.base/java.util.stream.ReferencePipeline$2$1.accept(ReferencePipeline.java:179)
at java.base/java.util.ArrayList$ArrayListSpliterator.forEachRemaining(ArrayList.java:1625)
at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310)
at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735)
at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:734)
at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:762)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeTestInstancePostProcessors(ClassBasedTestDescriptor.java:376)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$instantiateAndPostProcessTestInstance$6(ClassBasedTestDescriptor.java:289)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.instantiateAndPostProcessTestInstance(ClassBasedTestDescriptor.java:288)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$4(ClassBasedTestDescriptor.java:278)
at java.base/java.util.Optional.orElseGet(Optional.java:364)
at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$testInstancesProvider$5(ClassBasedTestDescriptor.java:277)
at org.junit.jupiter.engine.execution.TestInstancesProvider.getTestInstances(TestInstancesProvider.java:31)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$prepare$0(TestMethodTestDescriptor.java:105)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:104)
at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.prepare(TestMethodTestDescriptor.java:68)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$prepare$2(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.prepare(NodeTestTask.java:123)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:90)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at java.base/java.util.ArrayList.forEach(ArrayList.java:1511)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.invokeAll(SameThreadHierarchicalTestExecutorService.java:41)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141)
at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139)
at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138)
at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95)
at org.junit.platform.engine.support.hierarchical.SameThreadHierarchicalTestExecutorService.submit(SameThreadHierarchicalTestExecutorService.java:35)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestExecutor.execute(HierarchicalTestExecutor.java:57)
at org.junit.platform.engine.support.hierarchical.HierarchicalTestEngine.execute(HierarchicalTestEngine.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:107)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:88)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.lambda$execute$0(EngineExecutionOrchestrator.java:54)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.withInterceptedStreams(EngineExecutionOrchestrator.java:67)
at org.junit.platform.launcher.core.EngineExecutionOrchestrator.execute(EngineExecutionOrchestrator.java:52)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:114)
at org.junit.platform.launcher.core.DefaultLauncher.execute(DefaultLauncher.java:86)
at org.junit.platform.launcher.core.DefaultLauncherSession$DelegatingLauncher.execute(DefaultLauncherSession.java:86)
at org.junit.platform.launcher.core.SessionPerRequestLauncher.execute(SessionPerRequestLauncher.java:53)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.processAllTestClasses(JUnitPlatformTestClassProcessor.java:99)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor$CollectAllTestClassesExecutor.access$000(JUnitPlatformTestClassProcessor.java:79)
at org.gradle.api.internal.tasks.testing.junitplatform.JUnitPlatformTestClassProcessor.stop(JUnitPlatformTestClassProcessor.java:75)
at org.gradle.api.internal.tasks.testing.SuiteTestClassProcessor.stop(SuiteTestClassProcessor.java:62)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:568)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:36)
at org.gradle.internal.dispatch.ReflectionDispatch.dispatch(ReflectionDispatch.java:24)
at org.gradle.internal.dispatch.ContextClassLoaderDispatch.dispatch(ContextClassLoaderDispatch.java:33)
at org.gradle.internal.dispatch.ProxyDispatchAdapter$DispatchingInvocationHandler.invoke(ProxyDispatchAdapter.java:94)
at jdk.proxy1/jdk.proxy1.$Proxy2.stop(Unknown Source)
at org.gradle.api.internal.tasks.testing.worker.TestWorker$3.run(TestWorker.java:193)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.executeAndMaintainThreadName(TestWorker.java:129)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:100)
at org.gradle.api.internal.tasks.testing.worker.TestWorker.execute(TestWorker.java:60)
at org.gradle.process.internal.worker.child.ActionExecutionWorker.execute(ActionExecutionWorker.java:56)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:113)
at org.gradle.process.internal.worker.child.SystemApplicationClassLoaderWorker.call(SystemApplicationClassLoaderWorker.java:65)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.run(GradleWorkerMain.java:69)
at worker.org.gradle.process.internal.worker.GradleWorkerMain.main(GradleWorkerMain.java:74)
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: Failed to parse configuration class [com.msgs.main.MsgsApplication]
at app//org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:178)
at app//org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:415)
at app//org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:287)
at app//org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanDefinitionRegistryPostProcessors(PostProcessorRegistrationDelegate.java:344)
at app//org.springframework.context.support.PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(PostProcessorRegistrationDelegate.java:115)
at app//org.springframework.context.support.AbstractApplicationContext.invokeBeanFactoryPostProcessors(AbstractApplicationContext.java:771)
at app//org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:589)
at app//org.springframework.boot.SpringApplication.refresh(SpringApplication.java:733)
at app//org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:435)
at app//org.springframework.boot.SpringApplication.run(SpringApplication.java:311)
at app//org.springframework.boot.test.context.SpringBootContextLoader.lambda$loadContext$3(SpringBootContextLoader.java:137)
at app//org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:58)
at app//org.springframework.util.function.ThrowingSupplier.get(ThrowingSupplier.java:46)
at app//org.springframework.boot.SpringApplication.withHook(SpringApplication.java:1405)
at app//org.springframework.boot.test.context.SpringBootContextLoader$ContextLoaderHook.run(SpringBootContextLoader.java:545)
at app//org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:137)
at app//org.springframework.boot.test.context.SpringBootContextLoader.loadContext(SpringBootContextLoader.java:108)
at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContextInternal(DefaultCacheAwareContextLoaderDelegate.java:184)
at app//org.springframework.test.context.cache.DefaultCacheAwareContextLoaderDelegate.loadContext(DefaultCacheAwareContextLoaderDelegate.java:118)
... 87 more
Caused by: org.springframework.context.annotation.ConflictingBeanDefinitionException: Annotation-specified bean name 'userController2' for bean class [com.msgs.user.controller.UserController2] conflicts with existing, non-compatible bean definition of same name and class [com.msgs.msgs.jwt.controller.UserController2]
at app//org.springframework.context.annotation.ClassPathBeanDefinitionScanner.checkCandidate(ClassPathBeanDefinitionScanner.java:349)
at app//org.springframework.context.annotation.ClassPathBeanDefinitionScanner.doScan(ClassPathBeanDefinitionScanner.java:287)
at app//org.springframework.context.annotation.ComponentScanAnnotationParser.parse(ComponentScanAnnotationParser.java:128)
at app//org.springframework.context.annotation.ConfigurationClassParser.doProcessConfigurationClass(ConfigurationClassParser.java:289)
at app//org.springframework.context.annotation.ConfigurationClassParser.processConfigurationClass(ConfigurationClassParser.java:243)
at app//org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:196)
at app//org.springframework.context.annotation.ConfigurationClassParser.parse(ConfigurationClassParser.java:164)
... 105 more
너무 긴 에러는 눈에 잘 안들어옵니다.
읽히는 에러만 찾아서 해결해봅니다.
전 보통 제일 긴 부분이나 Caused by를 먼저 봅니다.
Caused by:
org.springframework.context.annotation.ConflictingBeanDefinitionException:
Annotation-specified bean name 'userController2' for bean class [com.msgs.user.controller.UserController2] conflicts with existing,
non-compatible bean definition of same name and class [com.msgs.msgs.jwt.controller.UserController2]
찾았습니다🫠.
package가 다르면 Controller 이름이 같아도 되겠지라고 생각한 저의 실수입니다.
같은 이름의 Bean을 등록할 수 없습니다.
🙋♀️
본 포스트는 공부 목적으로 작성하였습니다. 보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.
자원은 URI로, 자원의 행위는 HTTP Method로 표현 → 행위에 맞는 적절한 HTTP method를 사용한 것을 RESTful하다고 합니다.
조회: GET
생성: POST
일부 수정: PATCH / 전체 수정: PUT
삭제: DELETE
REST API 설계 규칙
소문자 사용
_ 대신 - 사용
마지막에 슬래시(/)를 포함하지 않음
행위를 포함하지 않음 행위는 URI 대신 Http Method를 사용하여 전달
파일 확장자는 URL에 포함시키지 않음
명사 사용, 예외적으로 컨트롤 자원을 의미하는 경우 동사 사용
URI에 작성되는 영어를 복수형으로 작성
// 기존
@RestController
@RequestMapping("user")
public class UserController {
@PostMapping("/signup")
public void userSignUp(@RequestBody User user) {
userService.signUp(user);
}
}
// 리팩토링
@RestController
@RequestMapping("api/v2/users")
@RequiredArgsConstructor
public class UserController {
@PostMapping("/new")
public String create(@RequestBody User user){
System.out.println("UserController.create");
Integer id = userService.create(user);
return id.toString();
}
}
signup → new
user → users
🙋♀️
본 포스트는 공부 목적으로 작성하였습니다. 보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mydb?serverTimezone=Asia/Seoul
username: DB 생성 시, 작성한 username
password: DB 생성 시, 작성한 password
+ 리팩토링
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=Asia/Seoul
username: DB 생성 시, 작성한 username
password: DB 생성 시, 작성한 password
useSSL=false
Establishing SSL connection without server's identity verification is not recommended 최신 mysql버전을 사용 시, SSL연결을 기본으로 사용 SSL을 사용하지 않을 경우, 명시하지 않으면 경고가 발생하므로 이를 제거하기 위해 추가
기존 JPA 관련 설정은 다음과 같습니다.
application.yml
jpa:
# Application 구동할 때마다, Entity로 정의된 테이블을 생성
generate-ddl: true
hibernate:
# 스키마 생성, 테이블 존재 시 변경된 부분만 반영
ddl-auto: update
show-sql: true
database: mysql
database-platform: org.hibernate.dialect.MySQL8Dialect
generate-ddl: true
데이터베이스 스키마를 자동으로 생성하거나 업데이트하는 기능을 활성화하는 설정
🚨 개발 및 테스트 환경에서 유용할 수 있지만, 운영 환경에서는 주의해서 사용
+ 리팩토링
jpa:
# Application 구동할 때마다, Entity로 정의된 테이블을 생성
generate-ddl: true
hibernate:
# 스키마 생성, 애플리케이션 실행 시 모든 table trop 후 create
ddl-auto: create
# 실행 sql문 콘솔에서 확인
show-sql: true
# formatter
properties:
hibernate:
format_sql: true
use_sql_comments: true
highlight_sql: true
# using database
database: mysql
# mysql 상세 지정
database-platform: org.hibernate.dialect.MySQL8Dialect
ddl-auto: update → create
개발 초기 단계이고, Entity 수정 예정이기에 애플리케이션 실행 시마다 table을 새롭게 만들고자 변경
formatter 추가
DDL이 출력된 모습이 다음과 같이 변경됩니다.
Hibernate: create table trip_schedule (schedule_id integer not null auto_increment, mod_date datetime(6), reg_date datetime(6) not null, user_id varchar(20) not null, city_name varchar(30), date_list varchar(500), primary key (schedule_id)) engine=InnoDB
+ 리팩토링
[Hibernate]
create table trip_schedule (
schedule_id integer not null auto_increment,
mod_date datetime(6),
reg_date datetime(6) not null,
user_id varchar(20) not null,
city_name varchar(30),
date_list varchar(500),
primary key (schedule_id)
) engine=InnoDB
🙋♀️
본 포스트는 공부 목적으로 작성하였습니다. 보시는 도중 잘못된 부분이나 개선할 부분이 있다면 댓글로 알려주시면 수정하도록 하겠습니다.