이번에 배우게 된 Spring + Vue를 활용해 간단한 주식 시장 웹서비스를 구현하고자 한다.
이 글은 구현한 애플리케이션의 구조 설명 및 시연 화면등으로 구성될 예정이다.
그 중 BackEnd의 Spring에 대한 설명이다.
Group - com. skala
Artifact - stock
Name - skala-stock
Type - Gradle (혹은 Maven 가능)
Java Version - 21
Packaging - Jar
Spring Boot - 3.x 버전
• Spring Web (REST API)
• Spring Data JPA
• H2 Database -> 파일저장 (또는 MySQL로 나중에 변경 가능)
• Lombok
• Validation
파일 구조
project_extracted/
stock/ ← Spring Boot 백엔드
build.gradle
settings.gradle
README.md
application.properties
controller/
PlayerController.class
StockController.class
TradeController.class
service/
PlayerService.class
StockService.class
TradeService.class
repository/
PlayerRepository.class
StockRepository.class
PlayerStockRepository.class
dto/
CreatePlayerRequest.class
CreateStockRequest.class
TradeRequest.class
PlayerResponse.class
StockResponse.class
domain/
Player.class
Stock.class
PlayerStock.class
config/
WebConfig.class
DataInitializer.class
GlobalExceptionHandler.class
templates/
...
PlayerController: 사용자 관련 요청 처리 (회원가입, 조회 등)StockController: 주식 관련 요청 처리 (주식 등록, 목록 조회 등)TradeController: 매수/매도 등 트랜잭션 처리PlayerService: 유저 생성/잔액 관리 등TradeService: 주식 거래 로직StockService: 주식 등록/조회PlayerRepository, StockRepository: 엔티티에 맞는 CRUD 기능PlayerStockRepository: 다대다 관계 관리CreatePlayerRequest, TradeRequest: POST 요청 바디 매핑PlayerResponse, StockResponse: 클라이언트에게 응답 전용 구조Player: 유저 테이블Stock: 주식 테이블PlayerStock: 보유 주식 매핑 테이블 (다대다 관계)(한명의 유저는 여러 개의 주식을 사고, 한 주식은 여러 유저가 동시에 보유할 수 있다)
중간 엔티티 PlayerStock을 둬서 @ManyToMany를 사용하지 않고 1:N + N:1로 구현
@OneToMany(mappedBy = "player", cascade = CascadeType.ALL, orphanRemoval = true)
private List<PlayerStock> stocks = new ArrayList<>();
→ 한 명의 유저가 여러 개의 PlayerStock 보유 가능
@ManyToOne
private Stock stock;
-> 여러개의 playStock -> 하나의 player / stock
WebConfig: CORS 설정 포함프론트엔드와 백엔드가 분리되어 있을때 거의 등장하는 개념
브라우저가 서로 다른 출처 간의 HTTP 요청을 제한하는 보안 정책
http://localhost:5173 (Vue 개발 서버)
http://localhost:8080 (Spring 서버)
@Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurer() {
@Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**") // 모든 경로에 대해
.allowedOrigins("http://localhost:5173")
// Vue 개발 서버(http://localhost:5173)에서 들어오는 요청만 허용
.allowedMethods("GET", "POST", "PUT", "DELETE", "OPTIONS")
// CORS 허용 메서드 지정
.allowedHeaders("*")
// 요청 헤더 종류를 제한하지 않음
.allowCredentials(true);
// 쿠키, 세션 정보, 인증 토큰 등을 포함한 요청 허용(인증처리가 필요한 경우)
}
};
}
DataInitializer: 초기 데이터 자동 삽입GlobalExceptionHandler: 에러를 JSON으로 반환 (Swagger 사용 시 주의)일관된 JSON 에러 메시지를 보내기 위해 사용
[깃허브 소스코드]