Skip to main content

[React + Spring + Kube] KOSTA 1


첫째 날에는 백엔드 그다음에는 Next.js로 프론트를 그다음에는 쿠벵네 배포하는 과정이다.

git 설정

git remote set-url origin https://ACCESS_TOKEN@저장소URL.git

스프링 시작


BookMarker라는 API 백엔드를 시작한다.

프로젝트 스펙은 다음과 같다.

Project : Maven
Language: Java 17 Dependencies: Spring Web, Spring Data JPA, Lombok, Dev Tools, Configuration Prociessor, Testcontainers, Flyway Migration, H2 Database, PostreSQL, Validation, Actuator

도메인


엔티티

어노테이션을 사용해서 엔티티를 정의하고 테이블을 정의했다.

id 값은 SequenceGenerator를 사용했다.

localhost:8080/h2-console 로 들어가면 데이터베이스 콘솔에 들어갈 수 있다.

repository


crud 레포지토리와 paging 레포지토리가 분리됐는데 JPARepository로 사용해서 둘다 사용할 수 도 있다.

여기에서 JPA 문서를 볼 수 있는데

public interface UserRepository extends JpaRepository<User, Long> {

@Query("select u from User u where u.emailAddress = ?1")
User findByEmailAddress(String emailAddress);
}

이런식으로 쿼리작성도 할 수 있다고 한다.

Service


어노테이션

@Transactional을 사용한다. 이 어노테이션 선언하면 자동으로 쉽게 트랜잭션 사용할 수 있게 해줌.

의존성 주입 DI

  • Setter Injection

    • 인스턴스 변수 위에 @Autowired,@Resource 선언
      • @Autowired : type base 객체 찾아서 주입
      • @Resource : Beand id로 객체 찾아서 주입
  • Constructor Injection

    • 생성자 주입
      • Mock을 만들 때 할 수 있음. Lombok의 RequiredArgsConstructor 어노테이션 사용

controller

그냥 생성했습니다.

runner

runner라는게 있음.

CommandLineRunner를 implements해서 사용하는데

Database에 값을 insert 하기 위해서 사용하기도 함.

@Order(숫자) 어노테이션으로 우선순위 정할 수 있음.

profile이 test인 경우에 @Profile("dev") 이런식으로 하면 runner가 실행하지 않습니다.

이런식으로 구분할 수 있다.

페이징


Controller와 Service를 수정해서 페이징 할 수 있다.

PageRequest를 사용해서 진행하면 된다.

dto


dto를 적용하는 이유는 SRP 때문이다.

entity를 그대로 사용하면 entity를 수정하고 암튼 번거로워지기 때문.

flyway


db 마이그레이션 툴이다.

db가 비워져있으면 schema history table 생성하고 상태를 추적한다.

resources/db/migration 파일들을 스캔한다.

prefix, version, separator, description, suffix로 구성된다.

prfix에서 V는 버전 마이그레이션 R은 반복, U는 취소 마이그레이션,

version은 숫자, 점 언더바 조합 ex- V2.1

description은 설명 칼럼에 추가

suffix 확장자인데 기본은 .sql

사용

spring.flyway.locations=classpath:/db/migration/{vendor}
# sql 기록
spring.flyway.baselineOnMigrate=true
# flyway로 형상 관리
spring.flyway.enabled=true

application.properties에서 이런식으로 설정해서 사용할 수 있다고 합니다.

FlywayAutoConfiguration 클래스가 있어서 vendor를 추적한다고 함.

그니까 spring.flyway.location 값에 있는 /db/migration/{vendor} 이렇게 돼있는데

실제 경로에서 저기에 vendor 자리에 h2가 있으면 h2로 만들어진다 이거임.

postgresql

docker hub에서 postgres docker를 가져와야한다.

docker pull postgres:태그명

그리고 아래처럼 실행할 수 있다.

docker run --name PostgreSQL -e POSTGRES_USER=유저명 -e POSTGRES_PASSWORD=비밀번호 -d -p 5432:5432 postgres:태그명

postgresql 실행

docker exec -it PostgreSQL bin/bash

이러면 postgreSQL 들어갈 수 있고

그 안에서도

db 접속

psql -U postgres -W

이렇게 해야 연결이 된다.

유저 생성

CREATE USER bookmark PASSWORD 'bookmark' SUPERUSER;

이렇게 유저 생성도 할 수 있고

\du

유저 리스트 확인 가능하다.

db 생성

CREATE DATABASE bookmarkdb OWNER bookmark;

확인

\l

연결

\c bookmarkdb bookmark

테이블 조회

\dt

처음에 하면 안되고 설정을 마치고 파일 작성하면 될 것이다.

dto projection


mapper 사용

mapper와 bookmarkDTO를 구현하여서 map 함수를 사용해서 가져오는 방법도 있다..

@Setter
@Getter
@AllArgsConstructor
public class BookmarkDTO {
private Long id;
private String title;
private String url;
private Instant createdAt;
}

@Component
public class BookmarkMapper {

public BookmarkDTO toDTO(Bookmark bookmark) {
return new BookmarkDTO(
bookmark.getId(),
bookmark.getTitle(),
bookmark.getUrl(),
bookmark.getCreatedAt()
);
}
}
public class BookmarkService {
private final BookmarkRepository repository;
private final BookmarkMapper mapper;

@Transactional(readOnly = true)
public BookmarksDTO getBookmarks(Integer page) {
int pageNo = page < 1 ? 0 : page - 1;
Pageable pageable = PageRequest.of(pageNo, 10, Sort.Direction.DESC, "id");

// 여기서 map 사용해서 적용
Page<BookmarkDTO> bookmarkPage = repository.findAll(pageable)
//.map(bookmark -> mapper.toDTO(bookmark));
.map(mapper::toDTO);
return new BookmarksDTO(bookmarkPage);
}
}

sql 사용

참고문서

public interface BookmarkRepository extends JpaRepository<Bookmark, Long> {
@Query("""
select new com.bookmarker.api.dto.BookmarkDTO(b.id, b.title, b.url, b.createdAt) from Bookmark b
""")
Page<BookmarkDTO> findBookmarks(Pageable pageable);
}

이렇게 repository에 find 메서드를 선언하고

 @Transactional(readOnly = true)
public BookmarksDTO getBookmarks(Integer page) {
int pageNo = page < 1 ? 0 : page - 1;
Pageable pageable = PageRequest.of(pageNo, 10, Sort.Direction.DESC, "id");
Page<BookmarkDTO> bookmarkPage = repository.findBookmarks(pageable);
return new BookmarksDTO(bookmarkPage);
}

이렇게 사용하면 된다.

test containers


통합 테스트를 하는 여러 방법이 있다.

local에서 올려서 test 하던가 in-memory로 하던가 embeded library도 있지만

test container를 활용하는 것이 추천 된다.

Search 기능 구현


JPQL

 @Query("""
select new com.bookmarker.api.dto.BookmarkDTO(b.id, b.title, b.url, b.createdAt) from Bookmark b
where lower(b.title) like lower(concat('%', :query, '%'))
""")
Page<BookmarkDTO> searchBookmarks(String query, PageRequest pageRequest);

이렇게 jpql을 사용해도 되고

JPA Query Method

JPA Query Methods 이 곳에서 findBy 메서드를 구경할 수 있다.

Page<BookmarkDTO> findByTitleContainsIgnoreCase(String query, PageRequest pageRequest);

이렇게 findBy method를 사용해도 된다.

Interface projection

Projections

JPA


jakarta.persistence = JPA

jakarta.validation = Java Bean

이런건가?

create


입력 항목 검증

problem-spring-web이라는 의존성 추가하면 오류 메시지 괜찮게 나온다.

cors


@Configuration
public class WebConfig {
@Bean
public FilterRegistrationBean<?> corsConfigurationSource() {
/// cors configuration 가져옴
CorsConfiguration configuration = new CorsConfiguration();

/// 오리진 모두 허용
configuration.setAllowedOriginPatterns(Arrays.asList("*"));

/// 크리덴셜 true
configuration.setAllowCredentials(true);

/// 헤더 허용
configuration.setAllowedHeaders(Arrays.asList(
"Access-Control-Allow-Headers",
"Access-Control-Allow-Origin",
"Access-Control-Request-Method",
"Access-Control-Request-Headers",
"Origin",
"Cache-Control",
"Content-Type",
"Authorization"));

/// 메서드 허용
configuration.setAllowedMethods(Arrays.asList("POST", "DELETE", "GET", "PATCH", "PUT"));

/// UrlBasedCorsConfigurationSource 클래스
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();

/// configuration 값을 적용
source.registerCorsConfiguration("/**", configuration);

/// CorsFilter에 이 설정값을 적용
FilterRegistrationBean<?> bean = new FilterRegistrationBean<>(new CorsFilter(source));
bean.setOrder(0);
return bean;
}
}

이런식으로 webConfig 생성해서 bean에 등록하여 사용

docker 배포


jar 생성

  1. 파일 이름 설정
    build 태그 안에 plugins 태그 밑에 finalName으로 설정한다.

    그리고 maven이나 gradel 업데이트

  2. 빌드

  • maven
mvnw clean package
mvnw clean package -Dmaven.test.skip=true

위처럼 하면 테스트 스킵

mvnw clean package -Dspring.profiles.active=dev

위처럼 하면 프로파일 지정

mvnw clean package -Denv=production

이렇게 하면 변수 지정

  • gradle
gradlew.bat clean bootJar
  1. 실행
java -jar target\springboot-nextjs-backend.jar --spring.profiles.active=prod

이런식으로 할 수 있다.

target.. 이거는 실행할 jar파일이고 profiles.active 설정은 profile 값을 뭐로할지 정하는거다.

docker 이미지 생성

  • 도커파일 생성

먼저 도커파일 생성하고

FROM openjdk:20
VOLUME /tmp
COPY target/springboot-nextjs-backend.jar springboot-nextjs-backend.jar
ENTRYPOINT ["java","-jar","/springboot-nextjs-backend.jar","--spring.profiles.active=prod"]

위처럼 작성하면 됩니다.

이미지는 openjdk를 사용하고

jar 파일을 복사해서

entry point 명령어들을 실행합니다.

  • 도커 이미지 빌드

도커 이미지 빌드 하고

docker build -t 도커유저이름/도커저장소이름:도커태그 .
  • 도커 허브 push

로그인하고

docker login -u 도커계정명

push

docker push 도커저장소:태그

도커저장소:태그는 이미지를 특정하는 거다.

github action 설정


yaml파일 작성하면 끝.