- 레고를 조립하듯 필요한 Module을 조립할 수 있다.
- N개의 Module이 조립되어 있는 프로젝트를 Multi Module 프로젝트라고 부른다.
- Multi Module 프로젝트 구조를 사용하는 이유
- A 서버와 B 서버에서 동일한 DB Entity가 필요할 경우 중복된 Entity를 Module화 시켜 사용하기 위해 Multi Module 프로젝트를 사용한다.
* 만일 각 서버마다 Entity를 생성해서 관리한다면, 변경 사항 발생 시 각 서버마다 해당 Entity를 수정해야 하기 때문에 리스크가 늘어난다. - Multi Module 구조에서 원하는 Module만 골라서 빌드&배포가 가능하다.
- A 서버와 B 서버에서 동일한 DB Entity가 필요할 경우 중복된 Entity를 Module화 시켜 사용하기 위해 Multi Module 프로젝트를 사용한다.
실습
1. Root Project를 생성한다.
- Root Project를 생성하고 필요 없는 폴더와 파일을 삭제한다.
- src 폴더와 help.md 파일을 삭제하였다.
2. API Module을 생성한다.
- Root Project 우클릭 -> New -> Module을 선택한다.
- Module 정보를 입력하고 Module을 생성한다.
- api-module이 multi-module-study 안에 생성된 것을 확인할 수 있다.
* common-module도 api-module과 마찬가지로 생성한 후, Server 용 Module이 아니므로 Application.java 파일을 삭제한다
3. Root Project - settings.gradle 수정
- Root Project에 생성한 Module의 정보를 include 한다.
4. api-module과 common-module 의존성 설정
- api-module이 common-module을 참조하기 때문에 api-module의 build.gradle에 implementation project('common-module')을 추가한다.
- 이때, implementation project에 기입할 project 명은 Root Project의 settings.gradle에 작성한 Module이름과 같아야 한다.
- Root Project의 settings.gradle에서 Module을 관리하기 때문에 api-module의 settings.gradle을 제거한다.
- api-module, common-module build.gradle에 tasks.register(prepareKotlinBuildScripteModel) {} 코드를 추가한다.
5. common-module에 Java 파일 생성 후 확인
@Getter
@AllArgsConstructor
public enum ErrorCode {
SUCCESS("200", "SUCCESS"),
UNKOWN_ERROR("-9999", "UNKOWN_ERROR");
private String code;
private String message;
}
- common-module에 공통적으로 사용할 ErrorCode Enum을 생성한다.
@Service
public class TestService {
public String testSuccess() {
return ErrorCode.SUCCESS.toString();
}
}
@RestController
@RequiredArgsConstructor
public class TestController {
private final TestService testService;
@GetMapping("/test")
public String testSuccess() {
return testService.testSuccess();
}
}
- apu-module에 TestController와 TestService를 생성한다.
- Server를 실행하고 curl localhost:8080/test를 입력하면 common-module에서 생성한 ErrorCode.SUCCESS 값이 반환되는 것을 확인할 수 있다.
- Module을 참조하면 해당 Module의 Java 파일을 사용할 수 있다.
6. common-module에 Bean 생성 후 확인
@Service
public class CommonService {
public String commonSuccessResponse() {
return ErrorCode.SUCCESS.toString();
}
}
- common-module에 CommonService Bean을 생성한다.
@RestController
@RequiredArgsConstructor
public class TestController {
private final TestService testService;
private final CommonService commonService;
@GetMapping("/test")
public String testSuccess() {
return testService.testSuccess();
}
@GetMapping("/common/test")
public String testCommonSuccessResponse() {
return commonService.commonSuccessResponse();
}
}
- api-module의 TestController에 testCommonSuccessResponse 함수를 생성한다.
- Server 실행 시, Parameter 1 of constructor in backstudy.apimodule.controller.TestController required a bean of type 'backstudy.commonmodule.service.CommonService' that could not be found. 다음과 같은 에러가 발생하는 것을 확인할 수 있다.
* Component Scan은 @SpringBootApplication이 존재하는 Class의 패키지 하위 내에서 이루어지기 때문에 해당 오류가 발생한다.
** 해결 방법1: ApiModuleApplication Class를 apimodule 패키지 상위로 이동시킨다.
ApiModuleApplication의 패키지 경로: backstudy.apimodule
common-module 내의 파일 경로: backstudy.~~
다음 상황에서 ApiModuleApplication을 상위 패키지로 옮기게 되면 backstudy에 위치하게 되어 common-module 내 파일을 Scan 할 수 있게 된다.
** 해결 방법2: @SpringBootApplication의 Component Scan 범위를 지정한다.
@SpringBootApplication(
scanBasePackages = {"backstudy.apimodule", "backstudy.commonmodule"}
)
7. 공통 Dependency 관리
plugins {
id 'java-library'
}
api 'org.springframework.boot:spring-boot-starter-data-jpa'
runtimeOnly 'com.h2database:h2'
- common-module의 build.gradle에 추가한다.
@Getter
@Entity
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Member {
@Id @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public Member(String name) {
this.name = name;
}
}
public interface MemberRepository extends JpaRepository<Member, Long> {
}
- common-module에 Member Entity, MemberRepository를 생성한다.
@RestController
@RequiredArgsConstructor
public class MemberController {
private final MemberRepository memberRepository;
@PostMapping("/save-member")
public Member saveMember() {
Member member = new Member("test1");
return memberRepository.save(member);
}
}
- api-module에 MebmerController를 생성한다.
- Server를 실행하면 Entity와 Repository를 Bean 주입받지 못해 발생하는 에러가 발생한다.
@EntityScan({"backstudy.commonmodule.entity"})
@EnableJpaRepositories(basePackages = {"backstudy.commonmodule.entity"})
public class ApiModuleApplication {
public static void main(String[] args) {
SpringApplication.run(ApiModuleApplication.class, args);
}
}
- ApiModuleApplication에 위와 같이 Annotation을 붙여주면, 해당 경로에 존재하는 Entity와 Repostiory를 주입받아 Server가 정상적으로 실행된다.
- localhost:8080/save-member를 호출하면 정상적으로 생성된 member를 응답받을 수 있다.
Project Build
tasks.bootJar { enabled = false }
tasks.jar { enabled = true }
- common-module의 build.gradle에 추가한다.
- bootJar를 true 설정하면 . jar 파일을 생성하는데 common-module의 경우 . jar 파일을 생성할 필요가 없어 false로 설정한다.
- bootJar가 true로 설정된 경우, Main 메서드를 찾아가는데 common-module의 경우 main 메서드를 제거했기 때문에 오류가 발생한다.
- jar = true 로 설정하면 xxx-plain.jar 파일을 생성하는데, 생성된 파일의 경우 Dependency를 가지지 않고 Class와 Resource만 포함하고 있기 때문에 Server를 실행시킬 수 없다.
* ./gradlew clean :api-module:buildNeeded --stacktrace --info --refresh-dependencies -x test 명령어로 프로젝트를 build 할 수 있다.
* jar 파일 실행 시, profile 설정은 -Dspring.profiles.active=[profile name] 명령어로 가능하다.
'정리 > 유용 기능' 카테고리의 다른 글
[소프트웨어 분석] Sonarqube (1) | 2022.11.20 |
---|---|
Android Studio 없이 Emulator 실행하기 (0) | 2022.10.20 |
유용한 도구 모음 (1) (0) | 2022.10.06 |
[IntelliJ] 클래스 생성 시 자동 주석 설정 (0) | 2022.09.06 |