결과 그러나 이 구현 방법의 문제점은 QuerydslRepositorySupport(코드상 ProductRepositorySupport)와 JpaRepository(코드상 ProductRepository)가 기능을 나눠 가져 항상 2개를 의존성으로 받아야 하는 문제가 있다
결과 이 구현 방법은 다른 경우보다 부담이 덜된다는 장점은 있지만 기본 JpaRepository와는 별개가 되기 때문에 기존 Repository의 메서드를 참조할 수 없게 된다. QueryDSL만 사용하게 되면 JPA로 간단히 사용했던 메소드를 구현해 줘야 한다는 단점이 있다. 그래서 프로젝트에는 QueryDSL과 Spring Data JPA를 같이 쓰는 방향으로 진행했다.
스프링 데이터 : 데이터베이스를 사용하는 데 도움을 주는 기술 (가장 많이 쓰는게 Spring Data JPA)
스프링 세션 : 세션 기능을 편리하게 사용할 수 있도록 도움을 주는 기술
스프링 시큐리티 : 보안
스프링 Rest Docs : API 문서와 테스트를 엮어 편리하게 도움을 주는 기술
스프링 배치 : 배치 처리에 특화된 기술
스프링 클라우드 : 클라우드에 특화된 기술
등등
☘️ 스프링 프레임 워크
핵심 기술: 스프링 DI 컨테이너, AOP, 이벤트, 기타 (강의의 초점!!)
웹 기술: 스프링 MVC, 스프링 WebFlux
데이터 접근 기술: 트랜잭션, JDBC, ORM 기원, XML 지원
기술 통합: 캐시, 이메일, 원격접근, 스케쥴링
테스트: 스프링 기반 테스트 지원
언어: 코틀린, 그루비
최근에는 스프링 부트를 통해서 스프링 프레임원크의 기술들을 편리하게 사용
☘️스프링 부트
스프링을 편리하게 사용할 수 있도록 지원, 최근에는 기본으로 사용
단독으로 실행할 수 있는 스프링 애플리케이션을 쉽게 생성
Tomcat 같은 웹 서버를 내장해서 별도의 웹 서버를 설치하지 않아도 됨
손쉬운 빌드 구성을 위한 starter 종속성 제공
스프링과 3rd party(외부) 라이브러리 자동 구성, 의존성 버전 관리
메트릭, 상태 확인, 외부 구성 같은 프로덕션 준비 기능 제공 (모니터링 제공)
관례에 의한 간결한 설정
스프링 부트는 스프링과 별도로 사용가능한 것이 아니다! 스프링 프레임워크 위에서 편리하게 기능을 사용할 수 있도록 돕는다!
☘️스프링 단어?
스프링이라는 단어는 문맥에 따라 다르게 사용된다.
스프링 DI 컨테이너 기술
스프링 프레임워크
스프링 부트, 스프링 프레임워크 등을 모두 포함한 스프링 생태계
☘️스프링 왜 만들었나요?
핵심 개념: 이 기술을 왜 만들었는가? 이 기술의 핵심 컨셉은? (로드존슨이랑 유겐 힐러가 왜 만들었을까!?!?!)
❓웹 애플리케이션 만들고, DB 접근 편리하게 해주는 기술?
❓전자정부 프레임워크?
❓웹 서버도 자동으로 띄워주고?
❓클라우드, 마이크로 서비스?
인걸까?? ❌ 이건 다 결과물일 뿐이다
* 스프링의 진짜 핵심
스프링은 자바 언어 기반의 프레임워크
자바 언어의 가장 큰 특징 - 객체 지향 언어
스프링은 객체 지향 언어가 가진 강력한 특징을 살려내는 프레임워크
스프링은 좋은 객체 지향 애플리케이션을 개발할 수 있게 도와주는 프레임워크
사람들이 스프링 등장에 열광했던 건 스프링 이전의 EJB는 객체 지향의 장점을 망치는 기술이었기 때문에 이걸 살릴 수 있었던 기술에 열광했던 거다!!
좋은 객체 지향 프로그래밍이란?
☘️객체 지향 특징
추상화
캡슐화
상속
다형성
☘️객체 지향 프로그래밍
객체 지향 프로그래밍은 컴퓨터 프로그램을 명령어의 목록으로 보는 시각에서 벗어나 여러개의 독립된 단위, 즉 "객체"들의 모임으로 파악하고자 하는 것이다. 각자의 객체는 메시지를 주고받고, 데이터를 처리할 수 있다. (협력)
혼자 있는 객체는 없다.
객체끼리 뿐만 아니라 수많은 객체 클라이언트와 객체 서버는 서로 협력 관계를 가진다. (요청과 응답하면서)
서버끼리도, 시스템끼리도!
객체 지향 프로그래밍은 프로그램을 유연하고 변경이 용이하게 만들기 때문에 대규모 소프트웨어 개발에 많이 사용된다.
유연하고, 변경이 용이하다?
객체 단위로 개발하기 때문에
레고 블럭 조립, 키보드 마우스 교체, 컴퓨터 부품 교체 처럼
컴포넌트를 쉽고 유연하게 변경하면서 개발할 수 있는 방법
이런 궁극의 유연함을 가질 수 있게 하는 객체 지향의 핵심! 다형성(Polymorphism) 이다.
☘️다형성(Polymorphism)
자동차라는 역할이 존재하고 이 자동차 역할에 그 어떤 종류에 자동차가 구현(K3든, 아반떼든, 테슬라든) 되어도 운전자는 자동차를 운전할 수 있다. 운전자는 자동차 역할이라는 인터페이스에 의존하고 있기 때문에 구현이 어떤게 와도 상관없다.
이는 운전자를 위한 것이다.
자동차 내부 동작 방식이 바뀌어도 운전자에게는 영향이 없기 때문에 쓰던 방식 그대로 쓸 수 있다. 운전자는 새로 뭔가를 배울 필요가 없다. 또한 구현체를 바꿔버려도 운전이 그대로 가능하니까 자동차 역할만 지키면 새로운 구현체도 들어올 수 있어 확장성이 생긴다.
이게 가능한 것이 역할과 구현을 나눠놨기 때문이다. (연극에서 하나의 역할을 여러사람이 할 수 있는 것도)
☘️ 역할과 구현을 분리
역할과 구현으로 구분하면 세상이 단순해지고, 유연해지며 변경도 편리
장점
클라이언트는 대상의 역할(인터페이스)만 의존
클라이언트는 구현 내부 몰라도 된다. 변경도 몰라도 되며, 심지어 구현 대상 자체가 바뀌어도 영향 X
프로그래밍 언어에서도 이러한 장점을 받아들이게 된다.
☘️ 자바 언어에서는 이걸 어떻게 받아들였을 까?
자바 언어의 다형성을 활용한 방식은 객체 설계시 역할과 구현을 명확하게 분리했다.
객체 설계시 역할(인터페이스)을 먼저 부여하고, 그 역할을 수행하는 구현 객체 만드는 방식으로 차용
역할 = 인터페이스
구현 = 인터페이스를 구현한 클래스, 구현 객체
역할 > 구현. 역할이 더 중요하다
오버라이딩 된 메서드가 실행될 때 다형성으로 인터페이스를 구현한 객체를 실행 시점에 유연하게 변경 할 수 있다.
상속 관계에서도 다형성, 오버라이딩 적용 가능
☘️ 다형성의 본질
인터페이스를 구현한 객체 인스턴스를 실행 시점에 유연하게 변경 할 수 있다.
다형성의 본질을 이해하려면 협력이라는 객체사이의 관계에서 시작해야한다.
클라이언트를 변경하지 않고, 서버의 구현 기능을 유연하게 변경할 수 있다.
☘️ 역할과 구현을 분리 : 정리
역할과 구현이라는 컨셉을 다형성을 통해 객체화 할 수 있다.
유연하고 변경이 용이
확장 가능
클라이언트에 영향을 주지 않는 변경이 가능
따라서, 인터페이스를 안정적으로 잘 설계하는 것이 중요하다.
☘️ 하지만 한계도 있다
역할(인터페이스)이 변하면 클라이언트, 서버 모두 변경이 발생한다. 그래서 더더욱 안정적인 인터페이스가 필요하다.
☘️ 스프링과 객체 지향
객체지향의 꽃은 다형성
스프링은 이런 다형성을 극대화해서 이용이 가능하게 한다.
스프링에서 말하는 제어의 역전(IoC), 의존관계 주입(DI)은 다형성을 활용해서 역할과 구현을 편리하게 다룰 수 있도록 지원해주는 역할을 해준다!
그래서 결론! 스프링을 사용하면 블럭 조립하든 구현을 편리하게 변경할 수 있다!
다형성이 스프링에 굉장히 중요한데 여기서 한가지가 더 있어야 완전히 스프링을 설명할 수 있다.
그게 바로 좋은 객체 지향 설계의 5가지 원칙, 소위 SOLID 라고 불리는 원칙이다.
좋은 객체 지향 설계의 5가지 원칙 (SOLID)
☘️ SRP (단일 책임의 원칙 / Single reponsibility principle)
한 클래스는 하나의 책임만 가져야 한다는 원칙. 하지만 하나의 책임이라는 게 모호하고 문맥과 상황따라 다를 수 있는 거라 중요한건 변경을 기준으로 한다. 변경의 이유를 하나만 가질 것 변경의 이유가 여러가지가 생겼다면 한가지 이상의 책임을 맡고 있는 것이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것이다.
☘️ OCP (개방-폐쇄 원칙 / Open-Closed principle)
소프트웨어 요소는 확장에는 열려있고, 변경에는 닫혀있어야 한다.
다형성을 활용해 이 원칙을 지킬수가 있다. 인터페이스를 구현한 새로운 클래스를 만들어 새로운 기능을 구현하면 확장에는 열려있다. 그리고 이 변화가 외부의 기존 코드에 변경을 주지 않는다. 변경에는 닫혀있는 것이다.
그런데, 개발 폐쇄 원칙은 문제점이 있다.
만약 MemberRepository m = new MemoryMemberRepository(); 를
MemberRepository m = new JdbcMemberRepository();로 구현 객체를 변경할 경우
클라이언트가 코드에서 변경을 해야한다. 다형성을 사용했으나 OCP 원칙을 지킬 수 없는데 이 문제를 해결하기 위해 객체를 생성하고, 연관관계를 맺어주는 별도의 조립, 설정자가 필요하다.
이 역할을 스프링의 컨테이너가 해준다! 이 원칙을 지키기위해 IoC(제어의 역전)도 DI(의존성 주입)도 필요한 것!
프로그램 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
이것은 단순히 코드에 상위 타입이 하위타입의 인스턴스로 교체를 성공하여 컴파일이 성공하는 걸 의미하지 않는다.
상위 타입의 객체를 하위 타입의 객체로 치환했다고 하더라도 상위 타입으로 동작하던 기존 프로그램은 정상적으로 동작해야하는 원칙이다. 상위의 엑셀 기능이 하위 타입으로 교체했을 때 후진 기능이 되버리면 리스코프 치환 원칙에 위배된 것이다. 하위 클래스는 인터페이스 규약을 다 지켜야한다. 그래서 다형성을 지원하기 위해 필요한 원칙으로 인터페이스를 구현한 구현체를 믿고 사용하려면 이 원칙이 필요하다.
☘️ ISP (인터페이스 분리 원칙 / Interface segregation principle)
특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
자동차 인터페이스 > 운전 인터페이스, 정비 인터페이스 로 분리
사용자 클라이언트 > 운전자 클라이언트, 정비사 클라이언트로 분리
이런식으로 분리하면 정비 인터페이스를 변경해도 정비사 클라이언트만 영향을 주지 운전자 클라이언트는 영향을 주지않는다. 기능을 적당한 크기로 자르는 것이 중요. 인터페이스(역할)이 명확해지고, 하나의 큰 역할이 아닌 보다 작은 역할들로 잘라낸 것이라 대체 가능성이 높아진다.
항해99 실전 프로젝트에서 게임 사이트를 제작하면서 화상 및 음성 채팅이 필요했고 우리는 WebRTC를 채택했다.
그런데 구현하는 과정에서 백엔드 입장에서 구현해야할 부분이 있었는 데, 백엔드는 뭘 해야줘야 하는 지부터 몰라서 많이 헷갈렸다. 공부를 하면서 백엔드는 WebRTC에 필요한 시그널링 서버를 구현해야 한다는 걸 알게되었고 따로 정리를 해보게 되었다 (백엔드 입장에서만 정리)
HTTP는 브라우저와 서버가 소통하는 프로토콜로 인터넷 데이터 교환의 가장 기본이 된다. 그런데 Http는 요청 방식을 보면 단방향이다. 브라우저가 요청하면 서버가 응답하고 그리고 끝!
상태를 유지하지도 않고 금방 우리가 뭐했는지도 잊어버리는 stateless 무상태로 돌아간다. 심지어 브라우저의 요청에만 반응하기 때문에 단반향이라 실시간에 적합하지 않아 뭐가 새로 업데이트를 했는지 보려면 계-속 뭐가 새로 바뀌었니 하면서 서버에 요청을 해야한다.
그래서 실시간성에 HTTP가 적합하지 않자 Websocket이 등장했다. Websocket은 브라우저와 서버간의 연결이 유지한다. 서로 계속 연결할 뿐만 아니라 양방향으로 가능한 통신이라 브라우저는 물론이고 서버도 요청없이 변화가 생기면 바로바로 브라우저에게 메세지를 보낼 수 있게 되었고 그 연결은 누군가 끊기 전까지 계속 된다.
하지만 웹소켓도 중개자인 서버가 필요하고 서버가 일일이 상대방을 포워딩해서 내용을 다른 사람에게 전달해야하다 보니 서버의 성능, 비용등의 많은 문제가 생긴다.
그럼 중개자가 없어지고 브라우저들 끼리만 연결하면 어떨까? 이게 바로 WebRTC가 하는 기능이다. 이름도 Web Real Time Communication의 약자인 이유가 서버 없이 서로 메세지, 음성, 영상등을 주고 받아 중개자인 서버가 없으니 속력이 빨라 실시간에 적합하기 때문이다.
📍WebRTC (Web Real-Time Communication) 란?
웹과 앱에서 별 다른 드라이버나 플러그인 없이 실시간으로 데이터를 교환할 수 있어 카메라와 마이크를 사용해 P2P(Peer to Peer) 실시간 커뮤니케이션이 가능을 하게 하는 기술
WebRTC는 별개의 플러그인을 설치하는 등의 귀찮은 작업도 필요없이 브라우저만 있으면 되기 때문에 편리하다. 게다가 서버는 계속 전달해줘야 하는 역할에서 빠지니 서버가 가져야하는 부담이 줄어 좋다.
그럼 잠깐 여기서 서버가 할일이 여기서 어딘데?
바로 WebRTC 시작지점에서 서버가 해줘야 할일이 있다. WebRTC는 그냥 이뤄지는 게 아니라 처음으로 서로에 대해 소개를 하는 시간이 필요하고, 이 부분에 있어서는 서버가 중개자로서의 역할을 해줘야 하는데 이를 시그널링 서버라고 한다.
📍WebRTC에 필요한 시그널링(Signalling) 이란?
서로 다른 네트워크에 있는 미디어 포맷 등을 상호 연동하기 위해 협의 과정을 거치는 데 이를 시그널링 이라고 한다. 이 과정에서 네트워크 주소 변환 및 방화벽에 대응하게 된다. 이 협의과정에서 중간 단계 역할을 하는 시그널링 서버를 백엔드가 구현해줘야한다!
브라우저에서 지원하는 API 종류로 서버와 클라이언트 간의 메세지 교환을 위한 통신 규약 (프로토콜). 단방향으로 한번 Request/Response를 보내면 사라지는 HTTP의 stateless 속성과 달리 연결지향 양방향 전이중 통신이 가능한 프로토콜로 연결을 지속하여 상태를 유지한다. 주로 데이터의 빠른 업데이트, 실시간을 위해 많이 사용된다.
✒ Session
브라우저(클라이언트)와 서버 간의 연결 상태로 브라우저가 웹서버에 접속한 시점부터 브라우저를 종료하여 연결을 끝낼 때까지 같은 사용자로 부터 오는 요청을 하나의 상태로 보면서, 그 상태를 일정하게 유지하는 기술. 웹 서비스에서 브라우저 당 하나의 Session을 갖는다.
✒ offer 와 answer
WebRTC 메세지는 아니지만 SDP라는 포맷으로 세션 정보를 생성하여 먼저 offer(제안)를 보내면 이에 대한 대답으로 answer(응답)를 보내며 서로 SDP를 교환한다.
✒ SDP (Session Description Protocol)
Peer가 가진 세션의 정보들을 포함하고 있다.
포함된 정보
SDP를 생성한 Peer의 식별자 (유저이름, 세션아이디, 세션 버젼, 네트워크 타입, 주소 타입, 유니캐스트 주소 등), 미디어라인 (미디어 타입(음성인지 영상인지등), 포트 번호, 미디어 형식(코덱) 등), 실시간 트래픽을 주고 받을 IP, RTCP에 사용될 IP, ICE 파라미터, DTLS 파라미터, BUNDLE행에 사용되는 식별자, 밴드위드스, 미디어 방향, opus 코덱 등등
웹 브라우저 간의 직접적인 Peer to Peer를 접속할 수 있도록 해주는 프레임워크. SDP가 미디어에 대한 정보를 준다면 네트워크에 대한 정보를 교환하는 것을 ICE candidate라고 한다. 각각의 Peer는 가장 좋은 후보에서 나쁜 후보 순서로 제안하며 서로가 맞는 최적의 주소값을 찾는다.