이 글은 스프링 부트 핵심 가이드(장정우 저)를 읽고 개인적으로 정리하기 위한 글입니다.

서버간 통신

  • 포털 사이트를 하나의 서비스 단위로 개발한다고 했을 때 서버를 업데이트하거나 애플리케이션을 유지보수 할 때 마다 사이트 작업 중입니다라는 팻말을 걸고 작업을 해야 합니다. 그만큼 개발에 보수적인 입장을 취할 수밖에 없고, 서비스 자체의 규모도 커지기 때문에 서비스를 구동하는데 걸리는 시간도 길어진다.
  • 이 같은 문제를 해결하기 위해 나온 것이 마이크로서비스 아키텍쳐(MSA) 말그대로 서비스 규모를 작게 나누어 구성한 아키텍쳐
    ex) 블로그 프로젝트, 카페 프로젝트, 메일 프로젝트등 애플리케이션을 기능별로 나눠서 개발하게 된다.
  • 각각 독립적인 애플리케이션을 개발 시 서비스 간에 통신해야 하는 경우가 발생(서버 간 통신)
  • 가장 많이 사용되는 방식은 HTTP/HTTPS

출처 : 스프링 부트 핵심 가이드


스프링부트의 동작 방식

  • 부트에서 spring-boot-starter-web 모듈을 사용하면 기본적으로 톰캣(Tomcat)을 사용하는 스프링 MVC 구조를 기반으로 동작
  • 서블릿 : 클라이언트의 요청을 처리하고 결과를 반환하는 자바 웹 프로그래밍 기술 (서블릿 컨테이너에서 관리) 서블릿 컨테이너는 서블릿 인스턴스를 생성하고 관리하는 역할 역할을 수행하는 주체로서 톰캣은 WAS의 역할과 서블릿 컨테이너의 역할을 수행하는 대표적인 컨테이너
    • 서블릿 객체를 생성, 초기화, 호출, 종료하는 생명주기 관리
    • 서블릿 객체는 싱글톤 패턴으로 관리
    • 멀티 스레딩을 지원
  • 스프링에서는 DispatcherServlet이 서블릿의 역할 수행
  • DispatcherServlet의 동작
    1. DispatcherServlet으로 요청(HttpServletReuqest)이 들어오면 DispatcherServlet은 핸들러 매핑(Handler Mapping)을 통해 요청 URI에 매핑된 핸들러(Controller)를 탐색한다.
    2. 핸들러 어댑터(Handler Adapter)로 컨트롤러를 호출
    3. 핸들러 어댑터에 컨트롤러의 응답이 돌아오면 ModelAndView로 응답을 가공해 반환
    4. 뷰 형식으로 리턴하는 컨트롤러를 사용할 때는 뷰 리졸버(View Resolver)를 통해 View(뷰)를 받아 리턴
핸들러 매핑?
요청 정보를 기준으로 어떤 컨트롤러를 사용할지 선정하는 인터페이스
  • 뷰 리졸버는 뷰의 렌더링 역할을 담당하는 뷰 객체를 반환
  • 이 책에서 다룰 애플리케이션은 뷰가 없는 REST 형식의 @ResponseBody를 사용 예정 뷰 리졸버를 호출하지 않고 MessageConverter를 거쳐 JSON 형식으로 변환해서 응답

스프링 부트 동작 구조



레이어드 아키텍처

애플리케이션의 컴포넌트를 유사 관심사를 기준으로 레이어로 묶어 수평적으로 작성한 구조를 의미
여러 방면에서 쓰이는 개념이며, 어떻게 설계하느냐에 따라 용어와 계층의 수가 달라진다.
일반적으로 레이어드 아키텍처라 하면 3계층 또는 4계층 구성을 의미
이 차이는 인프라(데이터베이스)레이어의 추가 여부로 결정

프레젠테이션 계층

  • 애플리케이션의 최상단 계층, 클라이언트의 요청을 해석하고 응답하는 역할
  • UI나 API 제공
  • 프레젠테이션 계층은 별도의비즈니스 로직을 포함하고 있지 않으므로 비즈니스 계층으로 요청을 위임하고 받은 결과를 응답하는 역할만 수행

비즈니스 계층

  • 애플리케이션이 제공하는 기능을 정의하고 세부 작업을 수행하는 도메인 객체를 통해 업무를 위임하는 역할을 수행한다.
  • DDD(Domain-Driven Design) 기반의 아키텍처에서는 비즈니스 로직에 도메인이 포함되기도 하고, 별도로 도메인 계층을 두기도 한다.

데이터 접근 계층

  • 데이터베이스에 접근하는 일련의 작업을 수행

레이어드 아키텍처의 특징

  1. 각 레이어는 가장 가까운 하위 레이어의 의존성을 주입 받는다.
  2. 각 레이어는 관심사에 따라 묶여 있으며, 다른 레이어의 역할을 침범하지 않는다.
    1. 각 컴포넌트의 역할이 명확하므로 코드의 가독성과 기능 구현에 유리
    2. 코드의 확장성도 좋아진다.
  3. 각 레이어가 독립적으로 작성되면 다른 레이어와의 의존성을 낮춰 단위 테스트에 용이

스프링의 레이어드 아키텍처 / 이미지 출처 : https://velog.io/@dnrwhddk1/Spring-레이어드-아키텍처


디자인 패턴

  • 소프트웨어를 설계할 때 자주 발생하는 문제를 해결하기 위해 고안된 해결책
  • 디자인 패턴이 모든 문제의 정답은 아니고 상황에 맞는 최적 패턴을 결정해서 사용하는 것이 바람직하다.

디자인 패턴의 종류

  • 디자인 패턴을 구체화해서 정리한 대표적인 분류 방식으로 GoF 디자인 패턴 이라는 것이 있다.

GoF 디자인 패턴 분류 / 출처 : https://velog.io/@namezin/GoF-design-pattern

  • GoF 디자인 패턴은 생성 패턴,구조 패턴, 행위 패턴의 총 3가지로 구분
  • 생성 패턴
    • 객체 생성에 사용되는 패턴으로, 객체를 수정해도 호출부가 영향을 받지 않게 됨
  • 구조 패턴
    • 객체를 조합해서 더 큰 구조를 만드는 패턴
  • 행위 패턴
    • 객체 간의 알고리즘이나 책임 분배에 관한 패턴
    • 객체 하나로는 수행할 수 없는 작업을 여러 객체를 이용해 작업을 분배. 결합도 최소화를 고려할 필요가 있음

생성 패턴

  • 추상 팩토리 : 구체적인 클래스를 지정하지 않고 상황에 맞는 객체를 생성하기 위한 인터페이스를 제공하는 패턴
  • 빌더 : 객체의 생성과 표현을 분리해 객체를 생성하는 패턴
  • 팩토리 메서드 : 객체 생성을 서브 클래스로 분리해서 위임하는 패턴
  • 프로토 타입 : 원본 객체를 복사해 객체를 생성하는 패턴
  • 싱글톤 : 한 클래스마다 인스턴스를 하나만 생성해서 인스턴스가 하나임을 보장하고 어느 곳에서도 접근할 수 있게 제공하는 패턴

구조 패턴

  • 어댑터 : 클래스의 인터페이스를 의도하는 인터페이스로 변환하는 패턴
  • 브리지 : 추상화와 구현을 분리해서 각각 독립적으로 변형케 하는 패턴
  • 컴포지트 : 여러 객체로 구성된 복합 객체와 단일 객체를 클라이언트에서 구별 없이 다루는 패턴
  • 데코레이터 : 객체의 결합을 통해 기능을 동적으로 유연하게 확장할 수 있게 하는 패턴
  • 퍼사드 : 서브시스템의 인터페이스 집합들에 하나의 통합된 인터페이스를 제공하는 패턴
  • 플라이웨이트 : 특정 클래스의 인스턴스 한 개를 가지고 여러 개의 ‘가상 인스턴스’를 제공할 떄 사용하는 패턴
  • 프락시 : 특정 객체를 직접 참조하지 않고 해당 객체를 대행(프락시)하는 객체를 통해 접근하는 패턴

행위 패턴

  • 책임 연쇄 : 요청 처리 객체를 집합으로 만들어 결합을 느슨하게 만드는 패턴
  • 커맨드 : 실행될 기능을 캡슐화해서 주어진 여러 기능을 실행하도록 클래스를 설계하는 패턴
  • 인터프리터 : 주어진 언어의 문법을 위한 표현 수단을 정의하고 해당 언어로 구성된 문장을 해석하는 패턴
  • 이터레이터 : 내부 구조를 노출하지 않으면서 해당 객체의 집합 원소에 순차적으로 접근하는 방법을 제공하는 패턴
  • 미디에이터 : 한 집합에 속한 객체들의 상호작용을 캡슐화하는 객체를 정의한 패턴
  • 메멘토 : 객체의 상태 정보를 저장하고 필요에 따라 상태를 복원하는 패턴
  • 옵저버 : 객체의 상태 변화를 관찰하는 관찰자들, 즉 옵저버 목록을 객체에 등록해 상태가 변할 때마다 메서드 등을 통해 객체가 직접 옵저버에게 통지하게 하는 디자인 패턴
  • 스테이트 : 상태에 따라 객체가 행동을 변경하게 하는 패턴
  • 스트래티지 : 행동을 클래스로 캡슐화해서 동적으로 행동을 바꿀 수 있게 하는 패턴
  • 템플릿 메서드 : 일정 작업을 처리하는 부분을 서브클래스로 캡슐화해서 전체 수행 구조는 바꾸지 않으면서 특정 단계만 변경해서 수행하는 패턴
  • 비지터 : 실제 로직을 가지고 있는 객체(visitor)가 로직을 적용할 객체를 방문하여 실행하는 패턴

REST API

  • 대중적으로 가장 많이 사용되는 애플리케이션 인터페이스
  • 이 인터페이스를 통해 클라이언트는 서버에 접근하고 자원을 조잘할 수 있음

REST?

  • Representational State Transfer의 약자로 WWW과 같은 분산 하이퍼미디어 시스템 아키텍처의 한 형식
  • 주고받는 자원(Resource)에 이름을 규정하고 URI에 명시해 HTTP 메서드(GET, POST, PUT, DELETE)를 통해 해당 자원의 상태를 주고받는 것을 의미

REST API?

  • API : Application Programing Interface, 애플리케이션에서 제공하는 인터페이스를 의미 API를 통해 서버 또는 프로그래매 사이를 연결할 수 있다.
  • REST API는 REST 아키텍처를 따르는 시스템/애플리케이션 인터페이스라고 볼 수 있다.
  • REST 아키텍처를 구현하는 웹 서비스를 RESTful하다 라고 표현

REST 의 특징

  1. 유니폼 인터페이스
    1. 일관된 인터페이스를 의미, REST 서버는 HTTP 표준 전송 규약을 따르기 때문에 어떤 프로그래밍 언어로 만들어졌느냐와 상관없이 플랫폼 및 기술에 종속되지 않고 타 언어, 플랫폼, 기술등과 호환해 사용할 수 있단느 것을 의미
  2. 무상태성
    1. 서버에 상태 정보를 따로 보관하거나 관리하지 않는다는 의미
  3. 캐시 가능성
    1. HTTP 표준을 그대로 사용하므로 HTTP의 캐싱 기능을 적용할 수 있음
    2. 이 기능을 이용하기 위해 응답과 요청이 모두 캐싱 가능한지 명시가 필요, 캐싱이 가능한 경우 클라이언트에서 캐시에 저장해두고 같은 요청에 대해서는 해당 데이터를 가져다 사용. 이 기능을 사용하면 서버의 트랜잭션 부하가 줄어 효율적이며 사용자 입장에서 성능 개선
  4. 레이어 시스템
    1. REST 서버는 네트워크 상의 여러 계층으로 구성될 수 있다. 그러나 서버의 복잡도와 관계없이 클라이언트는 서버와 연결되는 포인트만 알면 됨
  5. 클라이언트-서버 아키텍처
    1. REST 서버는 API를 제공하고 클라이언트는 사용자 정보를 관리하는 구조로 분리해 설계. 이 구성은 서로에 대한 의존성을 낮추는 기능

URI 설계 규칙 / 출처 : 스프링 부트 핵심 가이드

 

이 글은 스프링 부트 핵심 가이드(장정우 저)를 읽고 개인적으로 정리하기 위한 글입니다.

 

스프링 프레임워크(Spring framework)

자바 기반의 애플리케이션 프레임 워크로 엔터프라이즈급(기업 환경을 대상으로 하는 개발) 애플리케이션을
개발하기 위한 다양한 기능을 제공한다.

쉽게 얘기해 자바로 애플리케이션을 개발하는데 필요한 기능을 제공하고 쉽게 사용하도록 돕는 도구

 

스프링 부트

스프링은 목적에 따라 다양한 프로젝트를 제공하는데, 그 중 하나가 스프링 부트(Spring Boot)이다

 

스프링의 핵심 가치

애플리케이션 개발에 필요한 기반을 제공해서 개발자가 비즈니스 로직 구현에만 집중할 수 있게끔 하는것

 

제어 역전(IoC)

객체의 관리를 컨테이너에 맡겨 제어권이 넘어간 것을 제어 역전이라고 부르며, 제어 역전을 통해
의존성 주입(DI : Dependency Injection), 관점 지향 프로그래밍(AOP : Aspect-Oriented Programing) 등이 가능 해진다.

의존성 주입(DI : Dependency Injection)

  • 제어 역전의 방법 중 하나
  • 사용할 객체를 직접 생성하지 않고 외부 컨테이너가 생성한 객체를 주입받아 사용하는 방식
  • @Autowired 라는 어노테이션을  통해 의존성을 주입할 수 있음
  • 스프링 4.3 이후 버전은 생략도 가능 but 처음 스프링을 다룰 때는 가독성을 위해 어노테이션 명시 권장

스프링에서 의존성을 주입받는 3가지 방법

  1. 생성자를 통한 의존성 주입 -> 스프링에서 공식적으로 권장하는 방식
  2. 필드 객체 선언을 통한 의존성 주입
  3. setter 메서드를 통한 의존성 주입
생성자를 통한 의존성 주입
@RestController
public class DIController {

    MyService myService;

    @Autowired
    public DIController(MyService myService) {
        this.myService = myService;
    }

    @GetMapping("/di/hello")
    public String getHello() {
        return myService.getHello();
    }

}
필드 객체 선언을 통한 의존성 주입
@RestController
public class FieldInjectionController {

    @Autowired
    private MyService myService;

}
setter 메서드를 통한 의존성 주입
    @RestController
    public class SetterInjectionController {

        MyService myService;

        @Autowired
        public void setMyService(MyService myService) {
            this.myService = myService;
        }

    }

관점 지향 프로그래밍(AOP : Aspect-Oriented Programing)

  • 관점을 기준으로 묶어 개발하는 방식
  • 관점 : 어떤 기능을 구현할 때 그 기능을 핵심 기능부가 기능으로 구분해 각각을 하나의 관점으로 보는 것을 의미
    • 핵심 기능 : 비즈니스 로직을 구현하는 과정에서 비즈니스 로직이 처리하려는 목적 기능
                         ex)클라이언트로부터 상품 정보 등록 요청을 받아 데이터베이스에 저장하고, 그 상품 정보를
                              조회하는 비즈니스 로직을 구현한다면
                              (1) 상품 정보를 데이터베이스에 저장하고,
                              (2) 저장된 상품 정보 데이터를 보여주는 코드가 핵심 기능
    • 부가 기능 : ex) 로깅 처리, 트랜잭션 처리하려는 코드
  • AOP 구현 방법
    • 컴파일 과정에 삽입하는 방식
    • 바이트 코드를 메모리에 로듷는 과정에서 삽입하는 방식
    • 프락시 패턴을 이용한 방식
  • AOP는 모듈화해서 재사용 가능한 구성을 만들어 결론적으로 개발자가 비즈니스 로직을 구현하는 데만 집중할 수 있게 도와준다.

이미지 출처 : https://img1.daumcdn.net/thumb/R1280x0/?scode=mtistory2&fname=https%3A%2F%2Ft1.daumcdn.net%2Fcfile%2Ftistory%2F99F4E5475C722F6C09

스프링 vs 스프링 부트

  • 스프링은 모듈 추가 시 설정이 복잡하다.
  • 설정이 복잡해지는 문제를 해결하기 위해 등장한 것이 스프링 부트

스프링과 비교하여 스프링 부트가 가진 특징

  • 의존성 관리
    • 기존 스프링은 프레임워크나 라이브러리 버전을 올리는 상황에서 연관된 다른 라이브러리 버전까지도 고려해야 했다.
    • 부트에서는 spring-boot-starter 라는 의존성을 제공, 각 라이브러리의 기능과 관련해서 자주 사용되고 서로 호환되는 버전의 모듈 조합을 제공
    • 개발자는 라비르러리 호환 문제를 해결할 수 있다.
  • 자동 설정
    • 부트는 스프링 프레임워크의 기능을 사용하기 위한 자동 설정(Auto Configuration)을 지원
    • 자동 설정은 애플리케이션에 추가된 라이브러리를 실행하는데 필요한 환경 설정을 알아서 찾아 준다.
  • 내장 WAS
    • 부트의 각 웹 어플리케이션에는 내장 WAS(Web Application Server)가 존재
    • spring-boot-starter-web 의 경우 톰캣을 내장
  • 모니터링
    • 개발이 끝나고 서비스를 운영하는 시기에는 해당 시스템이  사용하는 스레드, 메모리, 세션 등의 주요 요소들을 모니터링 해야한다,
    • 부트에는 스프링 부트 액추에이터(Spring Boot Actuator)라는 자체 모니터링 도구가 있다.

문제


 

해결 방법


계속 원하는 값이 맞게 나오는데 틀렸다고 나와서 상당히 해맸다...
n이 10,000 까지인 것에 주의해서 문제에 접근을 해야 한다. long형으로 변수를 바꿔봐도 overflow가 발생하였다.
자바에서 BigInteger를 이용하여 문제를 해결할 수 있었다.

작성 코드


import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStreamWriter;
import java.math.BigInteger;

class Main{
    static BigInteger dp[];
    public static void main(String[] args) throws IOException{
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        BufferedWriter bw = new BufferedWriter(new OutputStreamWriter(System.out));

        int n = Integer.parseInt(br.readLine());

        dp = new BigInteger[n+2];
        dp[0] = BigInteger.ZERO;
        dp[1] = BigInteger.ONE;

        for(int i=2; i<=n; i++) {
            dp[i] = dp[i-2].add(dp[i-1]);
        }

        bw.write(String.valueOf(dp[n]));

        bw.flush();
        br.close();
        bw.close();
    }
}

'자료구조&알고리즘' 카테고리의 다른 글

[백준]1978번 : 소수 찾기  (0) 2022.07.17
[자료구조] HashSet(집합)  (0) 2022.07.16

문제


해결방법


여러 해결 방법이 있지만 요세푸스 문제의 대표적인 해결 방법인 Queue 자료구조를 이용하여 접근을 하였다.

1. 1 - N 번까지의 사람을 먼저 Queue에 순차적으로 넣어준다.
2. Queue에서 다 뽑을 때까지 계속 반복문을 돌린다 while 이용
3. K의 배수가 아닌 값은 Queue에서 뽑은 후에 다시 Queue에 넣는다.
4. 뽑은 값은 StringBuilder에 추가를 해준다.
5. 최종적으로 StringBulder를 출력을 해준다.

 

작성코드


import java.util.LinkedList;
import java.util.Queue;
import java.util.Scanner;

public class Main {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        //queue를 선언한다.
        Queue<Integer> queue = new LinkedList<>();

        int N = scanner.nextInt();
        int K = scanner.nextInt();

        //1 ~ N번까지 queue에 추가.
        for(int i =1;i<=N;i++) {
            queue.add(i);
        }

        //String을 계속 추가, 수정해야하므로 StringBuilder 선언 !
        StringBuilder people = new StringBuilder();
        people.append('<');

        //queue에서 사람을 다 뽑을 때까지 돌린다.
        while (queue.size()>1) {

            //K의 배수가 아닌 값은 뽑고 다시 넣는다.
            for(int i =0; i<K-1;i++) {
                int val = queue.poll();
                queue.offer(val);
            }
            //뽑은 값을 String에 추가
            people.append(queue.poll()).append(", ");
        }

        //마지막에 '>'로 닫아주고 출력
        people.append(queue.poll()).append('>');
        System.out.println(people);
    }
}

 

문제

 

해결방법


소수 : 1보다 큰 자연수 중 1과 자기 자신만을 약수로 갖는 자연수

이점에서 착안해서 2에서 N - 1로 하는 수를 나머지 연산했을 때 0으로 나눠 떨어지는 경우는 1과 자기 자신이외의 수를 약수로 갖는 것이기 때문에 이 경우에는 소수가 아니다라는 점을 착안해서 문제를 해결하였다.

 

작성코드


import java.util.Scanner;

public class Main {
    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);
        int N = scanner.nextInt();
        int primeCnt = 0;

        for (int i = 0; i < N; i++) {
            boolean isPrime = true;
            int num = scanner.nextInt();
	// 예외처리 1인 경우
            if (num == 1) {
                continue;
            }
			
            for (int j = 2; j <= Math.sqrt(num); j++) {
                if (num % j == 0) { // 소수가 아닌 경우
                    isPrime = false;
                    break;
                }
            }
		// 소수인 경우
            if (isPrime) {
                primeCnt++;
            }
        }
        System.out.println(primeCnt);
    }
}

'자료구조&알고리즘' 카테고리의 다른 글

[백준]10826번 : 피보나치 수4  (0) 2022.07.17
[자료구조] HashSet(집합)  (0) 2022.07.16

집합(Set)


 

특정 조건에 맞는 원소들의 모임
기본적으로 집합은 중복된 원소를 추가할 수 없다.

 

😃집합 표현 방법


  • 원소나열법
  • 조건제시법
  • 벤 다이어그램

원소나열법
A = {1, 2, 3, 4, 5}, B = {2, 4, 6, 8, 10}

조건제시법
A = {A | A는 정수, 1 ≤ A ≤ 5}
B = {2B | B는 정수, 1 ≤ B ≤ 5}

벤 다이어그램


집합의 종류

  • 합집합
  • 교집합
  • 차집합
  • 여집합

합집합

어느 하나에라도 속하는 원소들을 모두 모은 집합

합집합

교집합

두 집합이 공통으로 포함하는 원소로 이루어진 집합

교집합

차집합

A(or B)에만 속하는 원소들의 집합(ex A - B)

차집합

여집합

전체집합(U)에서 A의 원소가 아닌 것들의 집합

여집합


 

자바에서 집합 구현

import java.util.Arrays;
import java.util.HashSet;

public class Main {
    public static void main(String[] args) {

//      1. 자바에서 집합 사용 - HashSet
        System.out.println("== HashSet ==");
        HashSet set1 = new HashSet();
        set1.add(1);
        set1.add(1);
        set1.add(1);
        System.out.println("set1 = " + set1);
        set1.add(2);
        set1.add(3);
        System.out.println("set1 = " + set1);
        set1.remove(1);
        System.out.println("set1 = " + set1);
        System.out.println(set1.size());
        System.out.println(set1.contains(2));
        

//      2. 집합 연산
        System.out.println("== 집합 연산 ==");

//      2-1. 교집합
        HashSet a = new HashSet(Arrays.asList(1, 2, 3, 4, 5));
        HashSet b = new HashSet(Arrays.asList(2, 4, 6, 8, 10));
        a.retainAll(b);
        System.out.println("교집합: " + a);

//      2-2. 합집합
        a = new HashSet(Arrays.asList(1, 2, 3, 4, 5));
        b = new HashSet(Arrays.asList(2, 4, 6, 8, 10));
        a.addAll(b);
        System.out.println("합집합: " + a);

//      2-3. 차집합
        a = new HashSet(Arrays.asList(1, 2, 3, 4, 5));
        b = new HashSet(Arrays.asList(2, 4, 6, 8, 10));
        a.removeAll(b);
        System.out.println("차집합: " + a);

    }

}

'자료구조&알고리즘' 카테고리의 다른 글

[백준]10826번 : 피보나치 수4  (0) 2022.07.17
[백준]1978번 : 소수 찾기  (0) 2022.07.17

+ Recent posts