[Springboot] 개념 설명 (stream, request, stateless, stateful, session, 단방향, 전이중, 반이중) + 게시글 상세보기

김호정's avatar
Aug 19, 2024
[Springboot] 개념 설명 (stream, request, stateless, stateful, session, 단방향, 전이중, 반이중) + 게시글 상세보기
 

통신

서킷스위칭

notion image
 
A —————————— B
A 와 B 사이에는 물리적인 선이 있다. (Stream)
Stream에 전류가 계속 흐른다. 끊이지 않고.
 
A———————-D
A———————-C
A———————-B
서킷 스위칭 (회선 교환) ⇒ 시스템끼리 하나의 전용선을 할당받아 데이터를 주고받는 방식. 독립적인 통신 연결을 수립한다.
 
장점 : 빠르다. 데이터 통신 속도와 성능이 일정
단점 : 돈이 많이 든다.
 
그래서 이 방식을 안 쓴다.
대기업들이 자기들 본사 - 지사 연결 할 때 이 방식을 쓴다.
private 해서 보안에 좋기 때문.
 
근데 우린 돈 많이 들어서 안돼.
 

라운드로빈(Round-Robin(RR))

notion image
 
 
C 가 먼저 들어오면 B와 D는 단위가 작음에도 불구하고 기다려야 한다.
그래서 라운드 로빈 스케줄링을 적용해 시분할 시스템을 설계하기도 한다.
 
++
RR (라운드 로빈) 알고리즘을 사용하면, 시분할시켜서 시간의 형평성을 맞춘다. ⇒ “시분할 방식”
여러개의 프로세스가 CPU를 사용하기 위해 경쟁하는 환경에서
“CPU 사용 시간을 일정하게 할당” 하는 방식이다.
B → 1초
C → 1초
D → 1초
그리고 다시
B → 1초
C → 1초
D → 1초
정해진 시간 할당량 안에 완료되지 못한 프로세스는 큐의 맨 뒤에 배치된다. 차례를 기다리도록.

데이터의 header와 body

notion image
 
 
데이터를 받을때는 순서대로 들어오지 않는 경우가 있다.
위 그림의 박스에는 재조립을 위해서 번호가 붙어있어야 한다.
B가 1, 2, 3 데이터를 줬는데 A가 3, 2, 1로 받으면 이 번호로 재조립을 한다.
이때 이를 다시 재조립하기 위해서 1, 2, 3 … 이렇게 식별할 수 있는 번호를 붙여준다.
이 번호는 header에 담겨있고, 번호 외에 Ip 주소 등의 정보를 담는 곳이 ⇒ header
+ A가 2,1 만 받은 경우 재전송 요청을 하게 된다.
그 외 전송하는 본 데이터가 담겨있는 곳은 ⇒ body
 
Data ⇒ 바디
번호 및 Ip ⇒ 헤더
 
Http는 이걸 패킷에 담아서 전달한다.
패킷 안에 있는 데이터를 ‘바디’ 데이터라고 한다.
 
패킷 안의 데이터 = 바디 데이터
 
notion image
 
중간에 들어간 걸 라우터 라고 한다.
라우터의 역할 : 패킷 스위칭
 
우리가 다루는 데이터는 패킷으로 주고받는다
패킷 하나의 크기 = 최소 21바이트
 
데이터가 왔는데 1, 2, 3 데이터를 줬는데 A가 3, 2, 1로 받으면 이 번호로 재조립을 한다.
A가 2,1 만 받은 경우 재전송 요청을 하게 된다.
 
 

app/os/hw
notion image
 
모든 통신하는 애플리케이션은 포트가 필요하다. (ex 8080)
위의 동그라미(라우터) 마다 트레이스 를 다 한다.
라우터는 패킷 포워딩을 하는 역할을 한다.
 
어떻게 데이터를 보낼시에 대한 약속이 잇다
그래야 받는쪽에서 파싱할 수있다. (파싱 = 분석)
 
http
 
확장자 대신에 MIME 타입을 설정해서 보낸다.
.png 를 보낼 수 없다. → 이 데이터가 뭔지 표현하는게 MIME
 
내가 데이터를 보낼때는 BODY 데이터가 있으니까 HEADER에 뭔지 적어놔야 한다.
헤더에 적어두면 BODY에 뭐가 있는지 알려줄 수 있다.
 
App에서 데이터가 패킷형태로 흘러내려감
흘러내려가는 것 = Byte Stream (한 바이트 단위로 흘러내려감)
바이트 단위로 흘러 내려 감.
 
영어 1개 = 1byte
한글 1개 = 3byte
특수문자 1개 = 1byte
 
이니까
title=제목1&content=내용1 —→ 26 바이트
 
패킷 하나의 크기 = 약 21바이트
 
니까 위 데이터를 다 전송하려면 2개의 패킷이 필요.
 
x-www-form-어쩌고 로 보낼 수도 있고, Json 으로 보낼 수도 있다.
 

OS

OS는 ack 라는 신호 혹은 데이터를 수신한다.
패킷에 대한 응답으로 ack를 보낸다.
 
  • TCP : ack 가 안오면 데이터를 재전송 → 받은 쪽에서 끝까지 ack 응답이 안오면
보낸 데이터를 다 버려버림
  • UDP : ack 안받고 무조건 재전송해버림
 
 
안녕하세요를
안녕세요 라고 보내도 추측할 수 있다. 이런건 데이터가 유실되어도 알수있음.
 
사람이 보는 데이터는 UDP 로 보내도 된다 (눈으로 보고 추측할 수있으니까)
굳이 TCP 로 안해도 되지만 비밀번호 같이 유실되면 안되는 것 (전체 데이터가 다 있어야
하는것)은 TCP 로 보내자.
 
HTTP ← app 에서 설정
TCP (신뢰성 있는 통신을 위한 TCP..) ← OS에서 설정
 
이메일 SMTP 도 TCP를 사용한다.
 
notion image
 

 
http 는 response 응답 하고 선을 끊어 버린다.
→ stateless 통신 → 1번 요청했다가 선 끊기면 끝!
선이 끊긴 뒤로 db에 데이터 수정이 있으면 이를 동적으로 반영할 수 X
 
상태가 있는 서버
→ stateful → 소켓 통신이 stateful 이다. ( stateful 은 클라이언트의 상태를 저장.기억한다)
→ 선이 유지됨!
 
notion image
 
웹 소켓 → request, response . 선을 유지한다.
http → 선이 끊긴다.
 
  • 단방향 통신
  • 반이중 통신 → http 는 반이중 통신을 한다. (1번 요청/응답하면 선이 끊김)
  • 전이중 통신
 
view에 보여줄 데이터는 request에 담는다 ⇒ 응답하고 나면 필요 없으니까!
user 객체 같은건 session에 담는다.
 
http는 stateless 이다. → staleless는 서버가 요청자의 정보를 저장하고 있지 않는것 →
디폴트로 stateless 인데, session에 사용자의 정보가 저장해서 stateful 하게 만듦.
(session 사용하면 stateful 됨)
 
요새 트렌디한 서버는 전이중, stateful 로 만든다.
 
반이중은 클라이언트의 상태를 저장하지 않는다. (→ http 도 반이중임)
그래서 session(라카)을 이용해서 저장하고, statueful인척하고 쓴다.
 
 
http 1.0 get 만 있었음
 
http 1.1 get 외에 post, put, delete 가 추가됨
post로는 insert 요청,
put으로는 update 요청,
delete는 delete 요청을 한다.
 
네이버나 구글은 http 2.0 , 3.0을 쓴다.(⇒ 전이중) → 클라이언트가 요청 안해도 push 할 수 있다.
트렌디한 서버들은 다 이걸쓴다. f5 안해도 서버에서 push해줌. 도박사이트, 주식사이트…!
2.0 과 3.0은 다르다. 같은거 아님
 
지금까지 설명은 1.1
회사가서도 1.1 쓴다.
 
http는 요청을 하면 응답을 하고, 클라이언트의 상태를 저장하지 않는 stateless 상태입니다.
http는 연결을 끊어 버리는 반이중 이니까 클라이언트의 상태를 나중에 알지 못한다.
그래서 인증/인가를 위해 session을 사용해서 클라이언트의 상태를 저장하고
session을 사용함으로써 stateful한 구조를 가진다.
 
즉,
 
request는 클라이언트의 모든 정보를 저장하는 곳. (→응답할때 다 비워버림)
session은 클라이언트의 정보를 저장하는 곳.
내 정보를 저장할 수 있음. 사용자 정보를 저장하면 식별할 수 있다.
 
session id는 세션(락카) 번호 → 클라이언트가 들고 있음
다시 헬스장 와서 번호를 제시하면, 누군지 알아보고 입장을 허용한다.
 
 
heap, static, stack 말고
request 객체, session 객체의 개념을 서버에서 사용하니 잘 알아둘 것!
 
Stateless
Stateful
request(가방)
session(락카)
단방향
전이중
반이중(→ http)
 

단방향, 전이중, 반이중 개념

 
 
 
 
geolocation 자바스크립트
 

 
notion image
 
톰켓이 request 객체를 만든다.
 
notion image
 
응답할 때 request 객체 안의 데이터 ( 샴푸, 버릴 것 ) 다 사라짐
 
 
notion image
호밀밭 파수꾼.
스프링 프레임워크 안에 가둬놓고 거기서만 동작하게 한다.
 
스프링으로 젤 처음 들어오면 동작하는게 Distpatcher Servlet → 역할 : Controller 를 찾아주는 것
(⇒ 현재 패키지안에 어떤 컨트롤러가 있는지 다 훒어보고, 어디 매서드로 매핑할지
reflection으로 구현해둠)
 
스프링은 톰캣한테서 request를 전달 받는다. 이게 Dispatcher Servlet을 먼저 거친다.
 
이렇게 저렇게 db까지 갔다가 돌아와서
 
컨트롤러에서는 repository 가 보내는 model(데이터)을 받아서 (model이 request객체임)
뷰에다가 뿌려준다.
 
모든 웹 프레임워크는 위의 개념으로 동작한다
 
디스패쳐가 request 객체를 들고 있음!
 
 
이렇게 NoNo
이렇게 NoNo
딱 하나만 있어도 되는 싱글톤 패턴의 애들은 Ioc에 등록할 수 있지만,
Request (바구니) 처럼 여러개 존재하는 애들은 Ioc 에 등록할 수 없다.
⇒ 그래서 위처럼 적으면 안됨 (에러)
⇒ 즉, Ioc에 넣는건 다 싱글톤인 애들이고 Request는 싱글톤으로 관리되는게 아니라서
IoC에 넣을 수 없다.
 
이건 Ok
이건 Ok
 
Seeion은 IoC 에 등록 가능
notion image
Dispatcher Servlet은 request, response만 들고 있다!!!
→ session을 들고 있지 않음
 
request 객체를 통해서 session 메모리에 접근할 수 있음 (request contains session? No)
 
session이 필요하면, request가 session에 접근해서 가져온다.
→ Httpsession 하면 request가 접근할 수 있는 걸 다 주입해줌 (dependency injection)
 
Board 컨트롤러, Board 레파지토리, entity manager(em) → 3개를 Ioc가 들고있음
session 객체는 스프링에서 “1개” 존재한다 (→ session 안에 여러개가 들어가는 구조이지 session
자체는 1개가 존재. 해쉬맵처럼 내부에 저장할 수 있는 칸이 나뉘어져 있는거지 session 객체는
1개임. → 스프링이 꺼내쓰기 쉽게 할려고 이렇게 해둔건가? )
HttpSession은 @IoC에 넣어서 사용할 수 있다.
Ioc에 HttpSession을 넣어서 사용하면 우리가 편리하다.
 
notion image
 
request 객체는 어디서든 접근할 수 있다.
request pool은 톰캣이 만든다.
request pool은 대략 한 50개 정도 있다.
notion image
 
session의 Model(데이터)를 저장할 수 있지만,
모든 데이터를 session에 담으면 데이터가 소멸되지 않아서 메모리 관리가 안된다.
 
notion image
Model이 request 객체다 (Model에 넣는게 request(모든정보))
 
 
notion image
request객체(model)에 boardList를 저장해서 board/list 로 이동
→ 이때 request 객체를 return 한다거나 전달하는 형태가 아니라 request 객체에 models라는
이름으로 boardList 데이터를 저장해두고, view에서 request객체에 접근해서 꺼내서 쓰는 형태임
→ request 객체는 어디서든 접근할 수 있으니까!
 
list.mustache
mustache문법인데 지금 boardList 데이터가 5개 넘어왔으니까 반복문 써야하는데
{{#}}
{{/}}
이렇게 해주면 반복문이다. (간단)
 
notion image
 
mustache는 순수하게 request에 저장된 걸 뿌리는 기능만 가지고 있음
jsp처럼 <% %>해서 안에 자바코드 쓴다? 이런거 없음
→ jsp 방식을 계속 사용하면 view에서 if문 써서 계속 데이터를 변형할 수 있으니까?
애초에 데이터를 정제해서 view로 보내지 않는 나쁜 습관이 들 수 있어서 좋지 않다.
스파게티 코드를 쓸수도 있고…
그러니 완벽한 데이터 결정체를 가져와서 뿌리기만 하는 mustache를 쓰고 백엔드 실력을 늘리자.
 
++ data transfer object 어쩌고 하기에 굿 하다고 한다.
 
notion image
이렇게 /board 로 요청하면 성공적으로 데이터 조회가 된다.
 
💡
mustache가 request객체와 session객체에 접근할 수 있도록 아래와 같이 application.properties에 세팅해준다.
 
notion image
 

BoardController

notion image
 
model 데이터를 request 객체에 옮겨둔다.
request 객체를 저장해두었다가 view에서 동적인 부분이 필요할 때
request 객체에 저장해둔 model을 꺼내서 넣는다.
 
session과 request의 차이는 데이터 스코프
 
request에는 데이터베이스에서 가져온 걸 넣는다 (X),
session은 인증 인가를 할 때 사용한다(X)
 
이런게 아니라 (면접볼 때 이렇게 대답하면 안된다,,,)
 
요청과 응답이 끝났을 때 더 보관할 필요가 없는 데이터는 request객체에 저장
응답하고 나서도 지속적으로 계속 들고 있어야 하는 데이터(예. 클라이언트의 상태)는
session에 저장
 
이런 개념이다.
 
request로만 관리하면 stateless한 서버
session까지 사용해서 “기억”시키면 stateful한 서버라고 한다.
 
인증, 인가 때문에 session을 쓴게 아니다
stateless 한 서버를 stateful하게 만들려고 session을 사용하는거다!
 
 
notion image
1 - request 객체에 저장한 것
2 - session에 저장한 것
 
notion image
session에 저장한 것은 여기서 확인할 수 있다. (Application → Cookies )
위에 보이는건 방금 저장한 num
 
 

list.mustache

notion image
<h1> {{num}} </h1> ← Session값을 받는 문법
{{#models}} {{/models}} ← 반복문
{{title}}, {{id}} ← models의 데이터 값도 이렇게 꺼내면 된다.
 
 
 
💡
추가) 컨트롤러에서 redirect 하면 redirect되는 주소를 return한다. 그럼 클라이언트가 재요청하게 됨. 이때 request에 저장해서 redirect하면 다 사라지니까 request에 저장하면 안됨 이런 경우에는 session에 저장하고 redirect 해야함!!! @GetMapping(”/root”) public String root(HttpServletRequest request){ request.setAttribute(”model”, —-); return “redirect:/board”; } → 이렇게 하면 board로 클라이언트가 재요청할 때 model 찾을 수 없음
 
 
 
notion image
 
원래 board/1 이렇게 되어 있었는데 mapping 주소의 fix된 값을 위처럼 수정해줌 (컨트롤러 부분)
@ParhVariavle 왜 씀 ?
문법이 중요한게 아니라 왜 쓰냐가 중요하다!
 
설명 시작!
 
Get 요청 → DB select 요청
주소 설계 규칙 (→ 전세계 표준. 컨벤션. 서로의 약속임)
표준을 만들고 지키는건 에러때문이 아니라 협업을 편하게 하기 위해서임. 그러니 지켜주자.
 
프론트 개발자가 “이 주소를 넣으면 이 쿼리가 나올거야!” 하고 예측을 하고 때릴 수 있게
주소 설계 규칙을 지켜서 작성해준다. Api 문서 쓸때도 지켜라.
 
notion image
 
(1) /boards(테이블명)?title(컬럼명)=제목1 (← 쿼리스트링)
select * from board wehre title = ‘제목1’;
→ where절 이하 구체적인 건 쿼리스트링으로 날림
(→ ? 이하는 이 정보를 where 절 이하에 넣어서 조회해줘! 란 의미)
 
(2) /boards?title=제목1&content=내용1
select * from board where title = ‘제목’ and content = ‘내용1’;
 
→ x.www.form 어쩌고랑 동일하게 들어온다.
 
(3) /boards
select * from board where id = 1;
→ pk인 것은 pathvariable로 날린다.
→ unique한 것은 다 pathvariable로 날린다.
 
(4) /boards
select * from board
 
(5) /users/1/boards
유저 1번이 적은 모든 게시글 내놔
 
(6)/users/1/comments
유저 1이 적은 모든 댓글 내놔
 
(7) /boards/2/comments
2번 게시글의 모든 댓글 내놔
 
==⇒ 주소 설계 원칙을 지키니 주소에서 쿼리가 보인다!! 어떤 걸 요구하는 주소인지 파악하기 쉽다.
 
예) op.gg/champions/leesin/build?type=ranked
→ 챔피언의 username이 leesin인것 (그니까 leesin은 /users/1 의 1과 같은 pk의 값을 의미)
다음 테이블 이름은 build.
username이 leesin인 것(유니크함) ( ?로 안씀 ⇒ 유니크하니까 쿼리스트링을 안씀!!!!)
leesin의 build테이블의 컬럼인 type이 ranked인 것.
type은 pk도 아니고 unique도 아니고 일반 컬럼( 왜? 쿼리스트링을 썼으니까!!!!)
 
이렇게 주소를 이해해야 크롤링을 할 수있다.
주소 분석 하려면 기본적으로 전세계 표준을 알고 있어야 한다.
 
 
 
++
notion image
F12 → Network 로 들어가서 내가 요청한 주소를 클릭해보면
요청 헤더, 응답 헤더 등을 볼 수 있다.
 
Accept 는 이런 데이터를 달라고 하는 것 : text/html 은 html을 달라! 고 요청한 것. accept-encoding: 이부분은 데이터의 형태?를 의미 : gzip 이런건 ZIP 형태도 달라고 하는거
마지막에 User-agent 부분에서는 요청한 사람의 기기 정보? 등을 알 수 있다.
 
응답 헤더에 Content-type 은 이런 데이터를 응답한다고 하는 것 ( → Accept 와 반대되는 개념 ) content-type에 적혀있는 text/html; 이런게 아까 배운 MIME 이다.
 

MIME 타입 개념

 
 

상세보기 조회

BoardRepository

notion image
 
  • 조회이니까 @Transactional 안붙여도 OK
  • 쿼리문 쓰고 끝 부분에 Board.class 해주면 하이버네이트가 매핑해주기때문에
    • 내가 파싱 안해도 된다. ( 파싱하는게 그 JDBC 쓸때 ResultSet으로 받고 rs.next 해서 가져온 값을
      해당하는 변수에 하나씩 넣어주는 그 과정을 의미한다. 그거 안해도 JPA 쓰면
      하이버네이트가 자동으로 해준다)
  • 나중에 배울건데 relation도 애가 다 해준다.
  • query.getSingleResult 하면 Object 타입으로 결과가 나와서 (Board)로 자식으로 다운캐스팅 해줘야한다.
notion image
 

BoardRepository

notion image
null을 return 하도록 예외처리 해두면
 
notion image
BoardRepositoryTest 에 6 넣으면 (예외값) NullPointerException이 터진다. ( → 내가 그렇게 설정한 것)
 

BoardRepository

notion image
 
notion image
내가 처리하기 싫으면 throw로 던져서 나를 호출한 놈한테 다 던진다. (위임)
 
 
 
 
++
 
notion image
throws로 다넘겨서 (처음 호출한 놈(A)으로 다 throw해서) A에서 처리하도록 하는게 제일 편하다!
 
DS - C - R - em
만약 r이 throws 하면 c가 받고 또 throws하면 ds가 받음
모든 thorw를 DS 로 던져서 Dispatcher Servlet이 핸들링 하는게 제일 편하다고!!!
global Exception 으로 전체적으로 exception 핸들링 가능!
 
맨 앞단에서 또 throw 해서 에러를 던지면, 결국 JVM이 처리한다.
 
 
class A { public static void aGo() throws RuntimeException{ System.out.println("A호출됨"); B.bGo(); } } class B { public static void bGo() throws RuntimeException{ System.out.println("B호출됨"); C.cGo(); } } class C { public static void cGo(){ throw new RuntimeException("C오류남"); } } public class App { public static void main(String[] args){ try { A.aGo(); }catch (Exception e){ System.out.println("괜찮아"); } } }
C가 throw 한 에러를 B가 throws RuntimeException 해서 A에게 주고
A도 throws RuntimeException 해서 main 이 받아서 Exception 으로 catch
 
A호출됨
B 호출됨
괜찮아
 
이렇게 출력된다.
 

BoardRepositoryTest

notion image
가짜 매개변수에 6을 넣으면 내가 설정해 준 RunTimeException이 뜬다!
지금 RuntimeException이 뜨는건 괜찮다! 내가 알고 있는거니까.
나는 throw 해서 터트리는것 까지 해주고, 처리하는 방법은 나중에 배울 것!
 

BoardController

notion image
→ request 객체에 저장 (request.setAttribute) 해서 View에서 꺼내서 쓴다!
 

Detail.mustache

notion image
 
/board/{{model.id}}/delete
면접관이 “ 어 왜 주소 뒤에 동사를 붙였어요 ? “ 하고 물어보면 이렇게 대답하라!
 
지금 ajax 안쓰고 post로 delete 쓰고 있으니까,
주소설계의 마지막 부분에 /delete 추가해줬습니다.
 
{{>layout/header}} <div class="container p-5"> <!-- 수정삭제버튼 --> <div class="d-flex justify-content-end"> <a href="/board/{{model.id}}/update-form" class="btn btn-warning me-1">수정</a> <form action="/board/{{model.id}}/delete" method="post"> <button class="btn btn-danger">삭제</button> </form> </div> <div class="d-flex justify-content-end"> <b>작성자</b> : 익명 </div> <!-- 게시글내용 --> <div> <h2><b>{{model.title}}</b></h2> <hr/> <div class="m-4 p-2"> {{model.content}} </div> </div> </div> {{>layout/footer}}
 
마찬가지로
save-form.mustache 파일을 보면,
notion image
Ajax나 자바스크립트 없이 순수하게 POST/GET/DELETE/PUT 만 연습하기 위해서
Post로 등록, 수정, 삭제를 하면서 method를 다 post로 설정.
등록/수정/삭제를 구분하기 위해서 action = /board 뒤에 /save 라고 동사를 붙여준 것
 
 
 
 
 
 
Share article

keepgoing