• 인프런 스프링 입문 코드로 배우는 스프링 부트, 웹 MVC, DB 접근 기술 수강 중

스프링 통합테스트

  • 이전의 repository, service 테스트 코드는 spring과 상관없이 순수 java코드만 테스트한 것 (스프링 서버 시작 안함)
  • @SpringBootTest 어노테이션 붙이기 : 스프링 컨테이너와 테스트 함께 실행 (실제 스프링 구동)
  • 통합테스트 (integration test) : spring, db 등을 함께 구동하는 테스트
  • 단위테스트 (unit test): 이전에 했던대로 컨테이너 없이 기능단위로 테스트하는 것. 통합테스트보다 훨씬 속도가 빠르기 때문에 단위테스트를 잘 만드는 것이 중요함.

트랜잭션 (transaction)

  • db에 쿼리 실행하면 바로 반영되는 것이 아니라 commit을 해야 반영됨. 바로 반영되는 것처럼 보이는 것들도 사실 auto-commit이 되는 것
  • 쿼리 실행 후 commit해서 반영하기 전까지 쌓인 모든 논리적 연산들을 트랜잭션이라고 함.
  • @transational 어노테이션: 테스트 시작 전 트랜잭션 시작 -> 쿼리 실행 -> 항상 rollback -> DB 변경 없으므로 다음 테스트에 영향 주지 않음 (@Test에 붙었을 때만 rollback. 일반 service 등에 붙었을땐 정상동작)
  • test 후 commit하고싶으면 @Commit 어노테이션 붙이기

스프링 JDBC Template

  • JdbcTemplate, MyBatis 등 라이브러리는 JDBC API의 반복코드 대부분 제거해주지만 sql은 직접 작성해야 함. 여전히 실무에서 많이 사용.
  • SimpleJdbcInsert : 테이블명, 인덱스 컬럼명 받아서 insert query 알아서 생성

JPA (Java Persistence API)

  • ORM (Object-Relational Mapping) : 객체와 관계형 데이터베이스 테이블을 매핑하는 기술
  • jpa : 인터페이스만 제공하며 entity 매핑 필요함 => 구현체로 hybernate 사용
  • jpa 사용하기 위해 EntityManager 주입 받아야 함
  • pk 기준이 아닌 조회는 jpql이라는 쿼리 언어 사용 : 객체지향으로 쿼리 작성 가능
  • JPA 사용 시 데이터 저장 service에 항상 @Transactional 필요

application.properties 설정

show-sql=true   # jpa가 날리는 sql 쿼리 볼 수 있음
ddl-auto=none  # 테이블 없을 땐 class보고 알아서 테이블도 생성해주지만(create), 현재 있으므로 패스

에러 해결 : JdbcSQLSyntaxErrorException - column not found

  • 살짝 다르게 해본다고 넣어두고 안쓰던 member 클래스의 pw 변수가 테이블에는 존재하지 않아 생긴 오류
  • h2 데이터베이스에 ALTER TABLE MEMBER ADD PW VARCHAR(20) 실행해 컬럼 맞춰주니 해결됨

    javax.persistence.PersistenceException: org.hibernate.exception.SQLGrammarException: could not prepare statement
    at org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:154)
    at org.hibernate.query.internal.AbstractProducedQuery.list(AbstractProducedQuery.java:1626)
    at org.hibernate.query.Query.getResultList(Query.java:165)
    at com.ticktack.homeytest.repository.JpaMemberRepository.findByName(JpaMemberRepository.java:35)
    ...
    at com.ticktack.homey_test.service.MemberServiceIntegrationTest.회원가입(MemberServiceIntegrationTest.java:37)
    ...
    Caused by: org.h2.jdbc.JdbcSQLSyntaxErrorException: Column "MEMBER0
    .PW" not found; SQL statement:
    select member0.id as id1_0, member0.name as name2_0, member0.pw as pw3_0 from member member0 where member0.name=? [42122-200]
    ...

스프링 데이터 JPA (프레임워크)

  • 리포지토리 구현 클래스 없이 인터페이스만으로 개발 완료 가능
  • 기본적인 쿼리 기능들 다 제공됨 (ex. findAll, findById 등등)
  • 실무에서 JPA, 스프링 데이터 JPA를 기본으로 사용 + 복잡한 동적 쿼리는 Querydsl 라이브러리 이용
  • 더 복잡한 것은 JPA 제공 네이티브 쿼리 이용 / JdbcTemplate, mybatis 등등 섞어서 사용
  • 커스텀 메소드도 명명 규칙에 맞게 이름 작성 시 알아서 쿼리를 만들어 준다. Spring data jpa 메소드 명명 키워드

AOP (Aspect Oriented Programming)

  • AOP가 필요한 상황 : 모든 메소드의 호출 시간을 측정하고 싶다면?
  • 공통 관심 사항(cross-cutting concern) : 여러 메소드, 기능, 파일에 공통적으로 적용해야 할 사항
  • 핵심 관심 사항 (core concern) : 서비스 핵심 로직
  • 모든 메소드의 호출 시간 측정은 핵심 관심 사항은 아니지만 공통 관심 사항 -> 호출시간 측정 코드를 모든 메소드에 작성하기 어렵고 코드가 섞이면 유지보수하기 어려움
  • AOP 핵심: 공통 관심 사항핵심 관심 사항의 분리
  • 아래 코드에선 호출 시간 측정 로직만 따로 분리하여 작성함.
@Aspect
@Component // spring bean으로 등록
public class TimeTraceAop {

	@Around("execution(* com.ticktack.homey_test..*(..))") // 패키지 모든 곳에 적용 가능 (aop around로 검색)
	public Object execute(ProceedingJoinPoint joinPoint) throws Throwable {
		long start = System.currentTimeMillis();
		System.out.println("START : " + joinPoint.toString());
		// START : execution(List org.springframework.data.jpa.repository.JpaRepository.findAll())
		
		try {
			return joinPoint.proceed(); // 여기에서 기존 서비스 로직 등 실행되는 듯
		} finally { // try 이후 실행
			long finish = System.currentTimeMillis();
			long timeMs = finish - start;
			System.out.println("END : " + joinPoint.toString() + "  " + timeMs + "ms");
			// END : execution(String com.ticktack.homey_test.Controller.MemberController.list(Model))  222ms
		}
	}
}
  • 스프링 AOP 작동방식 : controller -> AOP가 적용되는 서비스에 도착하면 가짜(프록시)서비스 생성 -> aop 작동
profile
문서화를 좋아하는 개발자

0개의 댓글