본문 바로가기

JAVA/Spring

스프링부트의 동작 원리

1. 스프링은 내장 톰켓을 가진다. 톰켓을 따로 설치할 필요 없이 바로 실행가능하다.

 

-소켓과 http

소켓(socket): 운영체제가 가지고 있는 것으로, A라는 대상과 B라는 대상이 서로 메세지를 교환하고 싶을 때 운영체제가 제공해주는 소켓을 이용하게 된다.

 

처음에 A가 소켓을 연다(open)

오픈한 소켓의 포트번호가 필요한데, B가 A와 통신하고 싶다면 A의 IP주소를 넣으면 연결이 되고, A와 B는 서로 메세지를 주고 받는 통신이 가능해진다.

또 다른 대상인 C가 A와 통신하고 싶다면 B는 이미 소켓이 연결 되어있는 상태라 연결할 수 있는 방법이 없다.

그래서 최초에 연결용도로만 쓰는 포트를 만들고, 연결되는 순간 다른 포트를 만들며, 새로운 소켓을 만들 때는 새로운 스레드를 만든다.

최초에 연결 용도로만 쓰는 포트의 main스레드는 계속 새로운 사용자의 요청을 받는 역할을 하고,

새롭게 만들어진 소켓의 스레드는 통신하는 역할을 하는 것이다. 스레드가 있으면 시간을 쪼개서 동시에 동작하는 것처럼 보일 수 있다. 

*최초의 포트로 연결한 뒤 -> 최초의 포트와 연결을 끊고 다른 포트로 통신을 한다.

이런 소켓통신은 연결이 서로 끊어지지 않고 계속 유지되기 때문에 부하가 커서 느려질 수 있다.

 

http통신(web 통신)은 연결을 지속시키지 않고 끊어버리는 stateless 방식을 사용한다.

*http 통신은 단순하게 문서를 전달하는 통신이다.

stateless 방식은 A와 B가 C와 연결해서 데이터를 받고 싶다면 C는 소켓을 만드는게 아닌 데이터를 찾아서 돌려주고 더이상 통신할 수 없게 끊는다.

소켓 통신은 계속 연결되어 있기 때문에 부하가 크지만, 한 번 연결되면 계속 통신하기 때문에 누구와 연결 되었는지 계속 알 수 있다. http 통신은 부하가 적지만 새로 연결되면 계속 다른 사람으로 인식한다. 이런 단점을 보완하면서 만들어진 것이 웹서버이다.

연결이 지속될 필요가 없다면(예를 들어 정보만 얻으면 되는 경우) 소켓 통신이 아닌 http 통신을 사용한다.

http의 목표는 html이라는 확장자로 만들어진 문서를 필요한 사람에게 제공해주는 데에 있다. 즉, 문서 전달에 목적이 있다.

 

http는 운영체제가 가지고 있는 소켓을 이용해서 만들어졌는데, 이렇게 프로그램이 운영체제가 들고 있는 어떤 기능을 통해서 프로그램을 만들 때 불러서 사용하는걸 시스템 콜이라고 한다.

http 기반은 소켓이다. 톰켓과 웹서버의 차이를 알아야 하고, 웹서버에 대해 정확히 이해해야 한다.

친구들이 내게 데이터가 필요하다면, www로 이미 연결되어 있으니 인터넷을 통해 데이터를 줄 수 있냐고 물어봐야 하는 건 친구들이다. 즉 내가 갑이고 친구들은 을이다.

http, 웹서버는 갑이다. 갑이라는 건 을이 필요한 데이터를 가지고 있는 것이고, http 통신은 항상 을이 갑에게 request, 요청을 하는 구조이다. 요청하기 위해서는 컴퓨터 위치를 알아야 하기 때문에 IP 주소를 알아야 하고, 어떤 데이터가 필요한지를 정확히 명시해야 하기 때문에 이것은 URL로 만든다.

*url = 자원을 요청하는 주소, url의 가장 마지막에 있는 l이 location을 뜻한다.

request하면 갑은 해당 데이터를 response한다. 이것을 응답한다고 말한다.

응답할 때 갑은 을에게 데이터를 가져올일이 없기 때문에 을의 ip는 몰라도 된다. 갑은 항상 가만히 있다가 을이 요청할 때 보낸 정보를 토대로 그 정보에 응답하면 된다.

http에서는 이렇게 갑은 을의 ip 주소를 모른다. (요청할 때 응답만 하면 되는 구조이기 때문에)

따라서 요청하지 않을 때 응답하는 것은 불가능하다. 을의 주소를 알기 위해서는 소켓을 사용해야 한다. 소켓통신은 을이 연결하는 순간 계속 지속되기 때문이다. 소켓 통신으로 한 번 연결하면 갑이 을에게 원하는 시점에 언제나 데이터를 전달할 수 있다.

http 통신은 단순히 요청 시에 응답만 해주는 구조이다. 여기서 응답은 단순한 html 문서 혹은 특정 자원이며, 이런 자원은 전부 static 자원(정적인 자원)이라고 한다. 요청시마다 변하는 자원이 아닌 항상 정적이고, 이것이 웹서버이다.

 

-톰켓이란?

웹서버는 흔히 아파치를 사용한다. 컴퓨터에 특정한 폴더를 하나 지정하고 사람들에게 그것을 공유할 때 그 폴더 안에는 수많은 자원들이 있다. 어떤 사람이 자원을 요청한다며 request 하면 아파치는 그 자원을 응답해주면 끝난다. 

그런데 만약 요청한 자원이 .jsp파일이나 자바코드가 적혀있는 자원이라면 아파치는 자바코드를 이해하지 못하기 때문에 응답하지 못한다. 이 때 여기에 톰켓을 달게 된다.

톰켓을 달면 아파치는 자신이 이해하지 못하는 파일 요청은 톰켓에게 제어권을 넘긴다.

톰켓은 .jsp 파일 안에 있는 모든 자바 코드를 컴파일하고 컴파일이 끝나면 컴파일 된 데이터에 .html을 덮어 씌운다.

결국 톰켓이 하는 일은 자바코드를 컴파일하고 html 문서로 만들어주는 것이다.

그리고 이것을 다시 아파치에게 돌려주고, 아파치는 .html 파일을 응답해준다.

 

흔히 요청을 할 때 웹브라우저를 통해 요청하게 되는데, 웹브라우저로 .jsp 파일을 요청했을때 만약 .jsp 파일을 찾아서 돌려주기만 한다면 그냥 웹서버이다. 웹브라우저는 html, css, js와 같은 것들만 이해할 수 있기 때문에 웹브라우저가 요청한 데이터가 html 파일이면 응답받을시 예쁘게 열리지만 .jsp와 같은 파일을 응답받으면 이해하지 못하기 때문에 내용이 깨져서 보이게 된다. 이 때 톰켓으로 .jsp 파일을 html 파일로 바꾸면 정상적으로 파일을 열어 읽을 수 있게 해준다.

아파치는 요청한 파일을 응답해주는것 뿐이고, 톰켓은 요청한 파일 중 자바코드가 요청되면 그것을 컴파일해서 html 파일로 번역해 돌려주는 역할을 한다.

 

2. 서블릿 컨테이너

서블릿 컨테이너란?

클라이언트가 요청을 하면 서블릿 컨테이너와 톰켓이 요청을 받고,

요청이 최초의 요청이라면 객체를 생성하고 최초가 아니라면 객체를 생성하지 않고 이미 생성된 객체를 재사용한다.

 

요청(request)시에 정적인 파일(.html, .css, .png 등)을 요청한다면 아파치가 일을 하고, 자바파일을 요청하면 톰켓이 일을 하게 된다. 하지만 스프링은 .html, .css와 같은 파일들을 요청할 수 없기 때문에 필연적으로 톰켓이 일을 하게 된다.

요청시에 직접 요청하는 URL 방식을 막아놨기 때문에 URI를 사용하며, 무조건 자바를 사용해야 하기 때문에 아파치는 무조건 톰켓에게 제어권을 넘겨주게 된다.

  • URL: 자원에 접근할 때 사용하는 주소 요청 방식, http://naver.com/a.png 과 같은 형식이다
  • URI: 식별자를 통해 접근하는 방식, http://naver.com/picture/a 와 같은 형식이다. 파일이 아닌 식별자의 형식이며 이 요청에는 톰켓이 응답하게 된다.

request 요청을 할 때 요청이 자원이 아니라 자바와 관련된 자원이면 무조건 서블릿 컨테이너(톰켓)에 요청하고,

html이나 css일 경우 톰켓은 작동하지 않고 아파치만 작동한다.

 

이런 요청이 들어올때 서버가 최초에 하는 일은 서블릿 객체를 만드는 것이다. 서버를 실행하고 최초 요청이 들어오면 서블릿 객체를 만들고 그 안의 init()이라는 초기화 메소드를 호출한 다음 service() 메소드를 호출해서 post인지, get인지, put인지 등을 체크한다. get 작동방식이면 get()메소드를 호출한다.

출처 https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC/lecture/97747?tab=note&volume=1.00&quality=auto&speed=1.25

init()은 기존의 서버가 가진 스레드를 호출하지만,

service()는 새로운 스레드가 만들어지고 그 새로운 스레드가 get인지 put인지를 체크한다.

정리하자면, request(요청)이 되었을 때 그것이 자바와 관련된 요청이라면 서블릿 컨테이너가 요청되고, 이 때 최초의 요청이라면 서블릿 객체를 new해서 생성하고 init()이 호출되며, service()가 호출되기 직전 스레드를 하나 만들어서 실행하는 것이다. 실행을 하면 안에서 db연결이나 필요한 데이터를 찾아 html에 담아 응답하는 일들을 한다.

두번째 요청이 되면 이때는 서블릿 객체를 생성하지 않고 기존 객체를 재사용한다. 

요청이 왔을 때 init(), new()도 하지 않고 새로운 스레드가 만들어지고 그 스레드가 service()를 호출해서 체크한다.

 

자바의 메모리 영역에는 static, heap, stack이 있는데 만약 안에 hello()라는 메소드를 가진 클래스 A가 있다면

new A를 해서 객체를 생성하면 heap 이라는 공간에 hello() 메소드가 뜬다.

힙에 떠있는 상태에서 hello 메소드를 누군가가 호출하면 메소드는 호출 시에 stack 공간을 만들어서 hello 메소드의 내부 코드를 차례로 실행하며 필요한 메모리 공간으로 사용한다. 

[hello() 메소드의 스택 공간] 이 공간은 요청하는 사람마다 다르며 독립적인 공간이다. hello 메소드를 세번 호출하면 공간이 세개 만들어진다.

new를 하면 힙에 하나 뜨지만 메소드는 호출하게 되면 공유하지 않고 독립적으로 사용하게 되는 것이다.

그렇기 때문에 서블릿 객체가 하나만 있어도 메소드는 각자 따로따로 사용할 수 있다. (공간이 서로 다르기 때문에)

 

톰켓은 보통 스레드를 auto로 설정하면 스레드가 20개 만들어진다.

사용자 20명의 동시 요청이 오면 스레드를 20개까지 사용하다가 21번째 요청이 들어오면 대기하게 되고, 기존의 첫번째 스레드가 종료되면(=요청에 대한 응답이 끝나는 순간) 21번 스레드가 재사용된다. 이것을 pooling 기법이라고 한다.

 

정리하자면 다음과 같다.

  1. request 요청이 생긴다
  2. 최초 요청시 서버는 서블릿 객체를 만들고 필요한 메소드를 호출한다.
  3. 호출할때는 스레드가 하나 만들어진다.
  4. 응답(response)를 하게 되면 스레드는 할일이 없지만 제거되지않고 남겨둬서 나중에 재사용하게 된다.
  5. 새로운 요청이 들어오면 서블릿 객체는 다시 만들어지지 않고 서블릿 객체를 재사용해서 응답한다.
  6. 20명이 동시접근하면 스레드가 20개까지 늘어난다. 스레드가 메소드를 호출하고 응답한다.
  7. 동시 접근을 25명이 하게 되면 5명은 대기하게 된다. 대기하다가 response가 끝난 스레드를 재사용하게 된다. (pooling기술)
  8. 서블릿 객체도 스레드도 재사용을 하는데, 서블릿 객체는 1개이고, 스레드는 설정에 따라 몇개든 설정할 수 있다.

 

 

 

 

 

 

해당 게시글은 '최주호' 님의 스프링부트 개념정리 강의를 참고하였습니다.

출처 https://www.inflearn.com/course/%EC%8A%A4%ED%94%84%EB%A7%81%EB%B6%80%ED%8A%B8-%EA%B0%9C%EB%85%90%EC%A0%95%EB%A6%AC/dashboard

'JAVA > Spring' 카테고리의 다른 글

[스프링] 패키지 이름, UTF-8  (0) 2022.02.17
스프링 컨테이너  (0) 2022.01.08
스프링부트 JPA 개념잡기  (0) 2022.01.08
web.xml과 FrontController 패턴  (0) 2022.01.06
스프링이란? 스프링의 기초 개념  (0) 2022.01.06