-
Notifications
You must be signed in to change notification settings - Fork 2
프로젝트 기술 도입 목적
Spring은 웹 애플리케이션 구축을 위한 널리 사용되는 Java 프레임워크이지만 설정 및 구성하는 데 시간이 오래 걸릴 수 있습니다.
반면에 Spring Boot는 Spring을 훨씬 쉽게 시작할 수 있도록 해주는 Spring 위에 구축된 프레임워크입니다.
Spring Boot의 주요 이점 중 하나는 데이터베이스 연결, 로깅 및 보안과 같은 Spring 애플리케이션의 여러 측면을 자동으로 구성하는 기능입니다.
이렇게 하면 개발자가 이러한 구성 요소를 수동으로 설정하는 데 많은 시간과 노력을 절약할 수 있습니다.
Spring Boot에는 임베디드 서버가 포함되어 있어 개발자가 애플리케이션을 단일 실행 가능한 JAR 파일로 패키징할 수 있으므로 모든 시스템에서 애플리케이션을 쉽게 배포하고 실행할 수 있습니다.
Gradle은 Maven에 비해 빌드 시간이 더 빠르게 수행하고 특히 IntelliJ IDEA에서 Maven보다 더 나은 IDE 통합을 제공하기에 선택했습니다.
Jacoco와 SonarCloud는 Spring Boot 프로젝트의 품질과 유지 관리성을 개선하는 데 사용할 수 있는 두 가지 도구입니다.
Jacoco는 자동화된 테스트에서 프로젝트의 코드가 얼마나 많이 커버되는지에 대한 통찰력을 제공하는 코드 커버리지 도구입니다.
테스트되지 않은 코드 영역을 식별하고 프로젝트의 전반적인 품질을 개선하는 데 사용할 수 있습니다.
SonarCloud는 자동화된 코드 검토 및 분석을 제공하는 클라우드 기반 코드 품질 관리 플랫폼입니다.
코드 냄새, 보안 취약성 및 버그와 같은 문제를 감지하고 해결 방법에 대한 제안을 제공하는 데 사용할 수 있습니다.
이번 프로젝트에 Jacoco와 SonarCloud를 도입함으로써 얻은 이점은 다음과 같습니다.
-
Jacoco 및 SonarCloud를 GitHub Action에 통합함으로써 빌드 후 코드 품질 및 적용 범위 보고서가 자동으로 생성했다. 이렇게 함으로써 문제를 조기에 파악하고 프로덕션에 배포되지 않도록 방지할 수 있다.
-
코드의 전반적인 품질을 개선하기 위해 함께 협업할 수 있다.
- 데이터를 객체지향적으로 관리할 수 있기 때문에 개발자는 비즈니스 로직에 집중할 수 있고 객체지향 개발이 가능하다.
- 자바 객체와 DB 테이블 사이의 매핑 설정을 통해 SQL을 생성한다.
- 객체를 통해 쿼리를 작성할 수 있는 JPQL(Java Persistence Query Language)를 지원
- JPA는 성능 향상을 위해 지연 로딩이나 즉시 로딩과 같은 몇가지 기법을 제공하는데 이것을 잘 활용하면 SQL을 직접 사용하는 것과 유사한 성능을 얻을 수 있다.
-
SQL 중심적인 개발에서 객체중심적인 개발이 가능하다.
-
생산성이 증가한다.
- JPA에 객체를 전달만 하면 되므로 SQL을 작성하고 JDBC API를 사용하는 지루하고 반복적인 일을 JAP가 대신 처리해주어 생산성이 향상된다.
- 간단한 메서드로 CRUD가 가능해진다.
-
유지보수가 쉽다.
- 필드가 추가하거나 삭제되었다 하더라도 JPA가 일련의 과정을 대신 처리해주므로 수정해야 할 코드가 SQL을 직접 다룰때보다 현저히 줄어든다.
- 기존 -> 필드 변경시 모든 SQL 수정
- JPA -> 필드만 추가하면 된다. SQL은 JPA 가 처리한다.
-
Object와 RDB간의 패러다임 불일치 해결
-
상속 / 연관관계 / 객체 그래프 탐색 / 비교하기 같은 패러다임 불일치를 해결한다.
-
JAVA의 존재하는 상속관계를 객체의 상속관계를 지원하지 않는 데이터베이스에서 JPA는 아래방식으로 해결
-
-
JPA는 애플리케이션과 데이터베이스 사이에 다양한 성능최적화 기능을 제공한다.
-
관계형 데이터베이스마다 같은 기능에 대한 사용법이 다른 경우가 많다.
- 페이징처리는 데이터베이스마다 달라서 사용법을 배워야 하는데 JPA는 애플리케이션과 데이터베이스 사이에 추상화된 데이터접근 계층을 제공하여 애플리케이션이 특정 데이터베이스 기술에 종속되 않도록 도와준다.
@Query("select p from Post p join fetch p.user u "
+ "where u in "
+ "(select t from Follow f inner join f.target t on f.source = :user) "
+ "or u = :user "
+ "order by p .createdAt desc")
List<Post> findAllAssociatedPostsByUser(@Param("user") User user, Pageable pageable);
Spring Data JPA가 기본적으로 제공해주는 CRUD 메서드 및 쿼리 메서드 기능을 사용하더라도, 원하는 조건의 데이터를 수집하기 위해서는 필연적으로 JPQL을 작성하게 됩니다.
간단한 로직을 작성하는데 큰 문제는 없으나, 복잡한 로직의 경우 개행이 포함된 쿼리 문자열이 상당히 길어집니다.
JPQL 문자열에 오타 혹은 문법적인 오류가 존재하는 경우, 정적 쿼리라면 어플리케이션 로딩 시점에 이를 발견할 수 있으나 그 외는 런타임 시점에서 에러가 발생합니다.
이러한 문제를 어느 정도 해소하는데 기여하는 프레임워크가 바로 QueryDSL입니다.
QueryDSL은 정적 타입을 이용해서 SQL 등의 쿼리를 생성해주는 프레임워크입니다.
QueryDSL의 장점은 다음과 같습니다.
- 문자가 아닌 코드로 쿼리를 작성함으로써, 컴파일 시점에 문법 오류를 쉽게 확인할 수 있다.
- 자동 완성 등 IDE의 도움을 받을 수 있다.
- 동적인 쿼리 작성이 편리하다.
- 쿼리 작성 시 제약 조건 등을
BooleanExpression
메서드 추출을 통해 재사용할 수 있다. - QueryDSL을 사용하면 형식이 안전하고 이해하기 쉬운 방식으로 더 복잡하고 강력한 쿼리를 작성할 수 있으므로 매우 유용합니다.
Data JPA에서 즉시 사용 가능한 많은 기능을 제공하지만 사용자 지정 쿼리를 작성하거나 더 복잡한 작업을 수행해야 하는 경우가 있을 수 있습니다.
이럴 경우 QueryDSL과 함께 사용하여 해결할 수 있으며 여러 쿼리 언어와 함께 사용할 수 있으므로 다른 데이터베이스와 작업해야 하는 경우 매우 유용합니다.
쿠키와 세션을 활용한 로그인 구현 흐름은 다음과 같다.
- 클라이언트가 로그인 요청을 한다.
- 어플리케이션 서버에서는 요청값으로 DB에 해당 회원이 있는지 확인한다.
- 회원이 있다면 세션을 생성하여 서버에 저장한다.
- 세션값을 쿠키에 저장하여 클라이언트에 반환한다.
이렇게 하면 세션을 서버 내에서 보관하고 있기 때문에 개인 정보가 탈취될 가능성이 적다.
하지만 서버가 여러 개라면 어떻게 될까?
Client의 세션 값을 첫번째 어플리케이션 서버가 갖고 있다 가정하고
만약 로드 밸런서에 의해 다른 서버로 리소스를 요청 시, Client로 넘어온 Session Cookie 값이
해당 어플리케이션 서버에 관리를 하지 않고 있어 에러를 발생 시킨다.
이런 상황이 발생하는 이유는 클라이언트-서버 관계에서 Stateless가 보장되지 않았기 때문이다.
Stateless는 클라이언트와 서버 관계에서 서버가 클라이언트의 상태를 보존하지 않음을 뜻한다.
그런데 지금 상황은 어떤가? 서버 자체에서 회원의 세션 정보를 갖고 있다.
그럼 세션값 자체를 DB에 저장하면 되지 않나?
물론 그렇게 해도 된다. 하지만 트래픽이 많아졌을 때, 계속해서 DB 서버에 Session이 저장되고
API 리소스마다 Session을 조회한다면 DB 서버가 남아나지 않을 수 있다.
따라서 클라이언트-서버의 Stateless을 지키기 위해 토큰이란 개념을 사용한다.
- 사용자가 로그인을 요청
- 애플리케이션 서버에선 사용자 입력값을 바탕으로 토큰을 생성. 이때 토큰엔 사용자 정보가 암호화되어 들어가 있음. (accessToken엔 사용자 아이디, 회원 권한 이외에 민감한 정보는 담기지 않는다.)
- 어플리케이션은 총 2개의 토큰을 생성하는데 accessToken은 주로 유효기간이 짧기 때문에 DB에 저장되지 않고, refreshToken만 DB에 저장되어 사용된다.
- 만약 accessToken의 유효시간이 지나면 refreshToken을 통해 토큰을 재생성한다. 이때 refreshToken의 유효시간은 지나지 않은 상태이다.
이번 프로젝트에선 JWT을 활용한 로그인 구현 방식을 사용한다.
-
Session으로 이용해 로그인을 구현하는 방식
-
장점
- 보안성이 높다. 클라이언트 쪽에선 세션 ID만 저장되고 사용자의 중요한 정보는 서버에 저장되기 때문.
- 세션 ID가 노출되더라도 HTTPS 사용, 적절한 세션 타임아웃 등 조치를 취하면 보안 문제를 최소화할 수 있다.
-
단점
- 사용자가 많을 경우, 서버에 부담이 될 수 있다. 매 요청마다 서버에 세션 정보를 처리해야하기 때문.
- 애플리케이션 서버가 여러 개일 경우 세션 정보를 공유하는 어렵다.
-
장점
-
JWT를 이용해 로그인을 구현하는 방식
-
장점
- 서버 자원을 적게 사용한다. 클라이언트 측에서 JWT를 저장하고 있으므로 매 요청마다 서버에서 상태 정보를 가져올 필요가 없다.
- 애플리케이션 서버가 여러개라도 유연하게 사용할 수 있다. JWT는 클라이언트 측에서 관리하기 때문이다.
-
단점
- JWT는 클라이언트 측에서 저장하므로 보안에 취약하다.
- JWT 길이가 길어질 수 있다. JWT에 사용자의 정보를 저장하므로 길이가 길어질 수 있다. 이는 네트워크 상 전송 속도를 늦출 수 있다.
-
장점