의식의 흐름대로 정리

Java는 네트워크 상에서 쓸 수 있도록 미국의 썬 마이크로 시스템즈가 개발한 객체 지향 프로그래밍 언어

 

Java의 특징

  1. 자바가상머신(JVM)만 설치하면 컴퓨터의 운영체제(OS)에 상관없이 작동하기 때문에 운영체제에 독립적이다.
    - JVM(Java Virtual Machine) : 컴파일된 자바 바이트 코드를 실행시켜주는 소프트웨어

  2. 객체 지향 프로그래밍 언어이다. > 캡슐화, 상속, 다형성의 특징을 갖는다. > 코드의 재사용 및 유지보수에 용이하다.
    - 객체 지향 프로그래밍(OOP; Object-Oriented Programming) : 데이터를 객체로 취급하여 프로그램에 반영한 것으로, 기존의 절차 지향 프로그래밍과 다르게 객체의 상호작용을 통해 프로그램이 동작한다.
    - 객체 지향 프로그래밍과 절차 지향 프로그래밍 차이 : 절차 지향 프로그래밍은 순차적인 처리가 중요하며, 대표적으로는 C언어가 있다. 컴퓨터의 처리 방식과 유사하기 때문에 객체 지향 언어보다 처리 속도가 더 빠르다. 하지만 실행 순서가 정해져 있어 코드의 순서가 바뀌면 동일한 결과를 보장하기 어려워 유지보수가 어렵다. 또 디버깅이 어렵다. 객체 지향 프로그래밍은 코드의 재활용성이 높고 절차지향보다 코딩이 간편하다. 디버깅이 쉽다. 반면에 처리속도는 절차 지향 언어보다 느리며, 설계에 많은 시간이 소요된다. 절차 지향 언어는 실행 순서, 절차가 더 중점이고 객체 지향 언어는 필요한 객체들의 종류와 속성 등에 더 중점을 두고 프로그래밍 된다. 즉, 절차 지향과 객체 지향은 서로 프로그래밍 되는 방식이 다를 뿐, 반대되는 성질이 아니다.
    - 캡슐화 : 관련된 데이터와 메서드를 하나의 단위로 묶어 정보를 은닉하는 것(외부 객체에서 구현 방식은 알 수 없다). 클래스 필드 값에 권한을 설정할 수 있다(public, private, protected, default). getter/setter를 통해 접근하도록 가능하다.
    - public : 접근 제한 없음
    - private : 같은 클래스에서만 사용 가능(접근 권한이 가장 적음)
    - public > protected > default > private
    - 상속 : 부모 클래스에 자식이 접근할 수 있도록 물려받는 방식
    - 다형성 : 부모 타입의 참조 변수로 자식 타입의 객체를 다루는 것. 오버로딩과 오버라이딩이 있다.
    - 오버로딩 : 상속이 아닌 하나의 클래스 내에서 이름이 같은 여러 개의 메서드를 정의하는 것. 매개변수의 수, 배치(순서), 타입이 달라야 한다.
    - 오버라이딩 : 부모에게서 상속받은 것들을 새롭게 재정의 하는 것을 뜻함. 재정의 한 것은 자신의 클래스 내부에서만 영향을 끼치며 부모에게는 영향을 끼치지 않는다.

  3. Garbage Collector를 통해 자동적으로 메모리를 관리한다.
    - 시스템에서 더 이상 사용하지 않는 동적 할당된 메모리 블럭을 찾아 자동으로 다시 사용 가능한 자원으로 회수하는 것을 Garbage Collection이라고 하고, Garbage Collection을 수행하는 부분을 Garbage Collector라고 부른다.
    - 자바 메모리는 Method Area, Heap, Stack, PC Register, Native Method Stack 영역으로 나눈다.
    - Method Area는 JVM이 실행되면서 생기는 공간으로 모든 스레드가 공유하는 영역이다.
    - 런타임 상수풀(runtime constant pool), 필드(field) 데이터, 메소드(Method) 데이터 등을 분류하고 저장한다.
    - Heap 영역은 객체와 배열이 생성되는 영역이며, 힙 영역에 생성된 객체와 배열은 Stack 영역의 변수나 다른 객체의 필드에서 참조
    - 참조하는 변수나 필드가 없는 의미없는 객체는 Garbage Collector가 자동으로 제거한다.
    - 그래서 개발자는 객체를 제거하기 위한 별도의 코드를 작성할 필요가 없다.
    - Stack 영역은 각 스레드마다 하나씩 존재하며 스레드가 시작될 때 할당된다.
    - 메소드를 호출할 때마다 push 되고 메소드가 종료되면 제거(pop)된다. Last In First Out 방식이다.
    - PC Register는 스레드가 생성되면서 생기는 공간으로, 스레드가 처리하고 있는 명령어의 주소를 등록한다.
    - JVM이 실행하고 있는 현재 위치를 저장하는 역할
    - Native Method Stack은 Java가 아닌 다른 언어(C, C++)로 구성된 메소드 실행이 필요할 때 사용되는 공간이다.

  4. Multi thread를 지원
    - thread는 한 process 내에서 실행되는 동작(기능 function)의 단위
    - 각 thread는 속해 있는 process의 스택 메모리를 제외한 나머지 메모리 영역을 공유할 수 있다.
    - multi thread는 하나의 process가 동시에 여러 개의 일을 수행할 수 있도록 해주는 것.
    - 즉, 하나의 process에서 여러 작업을 병렬로 처리하기 위해 multi thread를 사용한다.
    - process는 프로그램이 메모리에 적재되어 CPU를 할당받아 실행되는 것
    - 예를 들어 유튜브를 실행하면 한 개의 프로세스가 실행되는 것.
    - 2개 이상의 process가 동시에 실행되면 multi process라고 한다.
    - cpu core가 1개라면 여러 프로세스를 짧은 시간 동안 번갈아 연산을 하는 동시성을 갖는다.
    - cpu core가 여러 개 라면 각각의 core가 각각의 process를 연산하는 병렬성을 갖는다.
    - multi process와 multi thread는 동시에 여러 작업을 수행한다는 측면에서 유사한 면이 있다.
    - 적용할 시스템에 따라 두 방법의 장단점을 고려하여 적합한 방식을 선택해야 한다.
    - multi thread는 multi process보다 적은 메모리공간을 차지하고 Context Switching이 빠르다.
    - 컴퓨터는 다양한 업무를 처리하기 위해 task를 번갈아 가며 실행하는데, 이 때 현재 진행하고 있는 task(process, thread)를 저장하고 다음 실행할 task의 상태를 읽어 적용하는 과정을 Context Switching이라고 한다.
    - multi process는 multi thread보다 많은 메모리 공간과 CPU 시간을 차지한다.
    - multi thread는 동기화 문제나 하나의 thread 장애로 전체 thread가 종료될 위험이 있다.
    - 동기화 문제란, 서로 다른 thread가 메모리 영역을 공유하기 때문에(자원을 공유하기 때문에) 여러 thread가 동일한 자원에 동시에 접근하여 엉뚱한 값을 읽거나 수정하는 문제이다.
    - multi process는 하나의 process가 죽더라도 다른 process에 영향을 주지 않아 안정성이 높다.
    - 메모리 구분이 필요할 때는 multi process가 유리하고, Context switching이 자주 일어나고 데이터 공유가 빈번한 경우, 자원을 효율적으로 사용해야 하는 경우에는 multi thread가 유리하다.


 

 

한 줄 정리

Java는 객체지향 프로그래밍 언어로써 캡슐화, 다형성, 상속의 특징을 갖습니다. 또 JVM(자바 가상 머신) 위에서 실행이 되기 때문에 운영체제에 독립적입니다.

 

객체지향 프로그래밍(OOP; Object-Oriented Programming)은 데이터를 객체로 취급하여 프로그램에 반영한 것으로, 기존의 절차지향 프로그래밍과 다르게 객체의 상호작용을 통해 프로그램이 동작합니다.

 

절차지향 프로그래밍은 순차적인 처리가 중요하며 대표적으로는 C언어가 있습니다. 컴퓨터의 처리 방식과 유사하기 때문에 객체지향보다 처리 속도가 빠릅니다. 하지만 실행 순서가 정해져 있어 코드의 순서가 바뀌면 동일한 결과를 보장하기 어렵고, 따라서 유지보수가 어렵습니다.
객체지향 프로그래밍은 코드의 재활용성이 높고, 절차지향보다 코딩이 간편합니다. 반면에 처리속도는 절차지향보다 느리며, 설계에 많은 시간이 소요됩니다.
절차지향은 실행 순서, 절차가 더 중점이고 객체 지향 언어는 필요한 객체들의 종류와 속성 등에 더 중점을 두고 프로그래밍을 합니다. 즉, 절차지향과 객체지향은 서로 프로그래밍 되는 방식이 다를 뿐, 반대되는 성질은 아닙니다.

 

캡슐화는 변수와 함수를 하나의 클래스로 묶어 정보를 은닉화하는 것으로, 외부객체에서 구현 방식을 알 수 없습니다. 접근 권한을 설정할 수 있으며, getter/setter를 통해 접근할 수 있습니다.

 

접근 권한에는 권한 순으로 public, protected, default, private이 있고, 접근 제한이 없는 public과 같은 클래스 내에서만 사용 가능하여 접근 권한이 가장 적은 private가 주로 쓰입니다.

 

다형성은 부모 타입의 참조 변수로 자식 타입의 객체를 다루는 것을 뜻합니다. 오버로딩과 오버라이딩 등이 있습니다.

 

오버로딩은 하나의 클래스 내에서 같은 이름의 메소드를 여러 개 정의하는 것으로, 매개 변수의 개수나 변수 타입이 달라야 합니다.

 

오버라이딩은 부모 클래스에서 상속 받은 것들을 재정의하는 것입니다. 재정의된 메서드는 같은 클래스 내에서만 영향을 끼치며, 부모에게는 영향을 주지 않습니다.

 

상속은 부모 클래스에 자식이 접근할 수 있도록 물려받는 것입니다. 기존 클래스의 기능을 유지하면서 추가적인 기능을 추가하여 클래스를 만들고 싶을 때 사용합니다. 상속은 코드를 간결화하며, 재사용성을 높일 수 있습니다.

 

JVM(Java Virtual Marchine)은 컴파일된 소스 코드를 실행시켜주는 소프트웨어입니다. JVM이 있어 운영체제에 상관없이 자바를 실행할 수 있습니다. JRE(Java Runtime Environment)는 자바 실행환경으로, 라이브러리 등 개발 되어있는 것을 실행합니다. JDK(Java Development Kit)는 개발을 위해 필요한 도구들을 포함하고 있어 개발 및 실행이 가능합니다.

 

자바의 메모리 영역은 Method Area, Heap, Stack, PC Register, Native Method Stack으로 나뉩니다.

 

Method Area는 JVM이 실행되면서 생기는 공간으로 모든 스레드가 공유하는 영역으로, 컴파일되어 들어온 코드를 분류하여 저장합니다.

 

Heap 영역에서는 Stack 영역의 변수나 다른 객체의 필드에서 참조하는 객체와 배열이 생성됩니다. 더 이상 참조하지 않게 되는 의미없는 객체는 Garbage Collector에 의해 제거됩니다. 

 

Stack 영역은 각 스레드마다 하나씩 존재하며 스레드가 시작될 때 할당됩니다. 메소드를 호출할 때마다 push되고 종료되면 마지막 메소드부터 제거(pop)됩니다(Last In First Out)

 

스레드(thread)는 한 프로세스(process) 내에서 실행되는 동작의 단위이고, 프로세스는 CPU를 할당받아 실행되는 프로그램을 뜻합니다. 예를 들어 유튜브를 키면, 한 개의 프로세스가 실행되는 것입니다.

 

PC Register는 스레드가 생성되면서 생기는 공간으로, JVM이 실행되고 있는 현재 위치를 저장하는 역할을 합니다.

 

Native Method Stack은 자바 외의 언어로 구성된 메소드 실행이 필요할 때 사용되는 공간입니다.

 

자바는 멀티 스레드를 지원합니다. 멀티 스레드란, 하나의 프로세스에서 여러 개의 작업을 병렬로 처리하는 것을 말합니다.

 

프로세스도 두 개 이상의 프로세스가 동시에 작동할 때 멀티 프로세스라고 합니다. CPU core를 한 개만 사용할 때는 여러 프로세스를 짧은 시간 동안 번갈아 연산하는 동시성을 갖습니다. CPU core가 여러 개라면, 각각의 코어가 각각의 프로세스를 연산하는 병렬성을 갖습니다.

 

멀티 스레드와 멀티 프로세스는 동시에 여러 작업을 한다는 측면에서 유사하지만, 장단점이 다릅니다.
멀티 스레드는 멀티 프로세스보다 적은 메모리 공간을 차지하고, Context Switching이 빠릅니다. 반면 멀티 프로세스는 멀티 스레드보다 많은 메모리 공간과 CPU 시간을 차지합니다.
멀티 스레드는 동기화 문제나 하나의 스레드 장애로 전체 스레드가 종료될 위험이 있지만, 멀티 프로세스는 하나의 프로세스가 죽더라도 다른 프로세스에 영향을 주지 않아 안정성이 높습니다.

 

메모리 구분이 필요할 때는 멀티 프로세스가 유리하고, Context switching이 자주 일어나고 데이터 공유가 빈번한 경우, 자원을 효율적으로 사용해야 하는 경우에는 멀티 스레드가 유리합니다.

 

Context switching은 컴퓨터가 task를 번갈아 가며 실행할 때, 현재 진행하고 있는 task를 저장하고 다음 실행할 task의 상태를 읽어 적용하는 과정입니다.

 

동기화 문제란, 서로 다른 스레드가 메모리 영역(자원)을 공유하기 때문에, 동일한 자원에 동시에 접근하여 엉뚱한 값을 읽거나 수정하는 문제 입니다.

 

728x90

'이론 > CS공부' 카테고리의 다른 글

컬렉션 프레임워크  (0) 2022.11.02
추상화  (0) 2022.11.02
MVC패턴  (0) 2022.11.02
Cookie vs Session  (0) 2022.10.31
Servlet vs JSP  (0) 2022.10.27
국비 수업 시간에 조모임으로 발표했던 OSI 7계층

 


친구들과 주고받는 카톡, 아무 생각 없이 받는 다운로드 과정에서 데이터는 어떻게 움직이는 것일까?

다음의 OSI7계층을 통해 그 흐름을 파악할 수 있다.

 

데이터는 7계층에서 1계층까지의 과정을 거쳐 상대방의 컴퓨터에 도달하고,

다시 1계층에서 7계층까지의 과정을 거쳐 상대방에게 보여진다.

 

네트워크는 처음 미국 국방부 산하에서 만들어졌고(아르파넷), 국방부에서만 사용 시에는 문제가 없었다.

하지만 다른 민간 기업들이 사용하게 되고 서로 다른 네트워크를 사용하게 되면서,

서로 다른 제조사의 네트워크 장비들 간에서는 통신에 어려움이 생기었다.

따라서 표준화된 모델이 필요했고, 이에 따라 나온 것이 인터넷을 사용할 때 따라야 할 규칙이다.

 

OSI 7계층은 흐름을 한 눈에 알아보기 쉽게 만들어졌고,

각 계층별로 층이 나눠져 있기 때문에 한 층에 생긴 문제가 다른 층에 영향을 주지 않는다.

따라서 유지보수가 편하다는 장점이 있다.

 

하지만 사실 오늘날 따르고 있는 인터넷 통신 규칙은 OSI 모델이 아닌 TCP/IP 모델이다.

왜일까? 이유는 아래에...

 

OSI 7계층은 기술의 가용성을 고려하지 않은 이론적인 모델이기 때문에 구현에 있어 제한사항이 많다.

무엇보다 출시 시기가 TCP/IP 모델보다 늦어 이미 TCP 모델이 장악하고 있는 네트워크 시장에서 사용되기가 쉽지 않았다.그럼에도 우리는 OSI 7계층을 중요하게 배우는데 이유는 다음과 같다.

 

데이터의 흐름을 각 계층별로 잘 정리해 놓아 공부하기 좋고,

오늘날 네트워킹 장치를 만들 때 기본적으로 OSI 7계층을 참조해서 만들기 때문에 OSI 7계층을 배워야 한다.

 

기존의 OSI 모델과 TCP/IP 모델이다.

가장 오른쪽의 TCP/IP 모델이 최신 모델인데,

OSI 모델의 영향을 받은 것을 확인할 수 있다.

 

이제 본격적으로 데이터가 어떤 경로를 거쳐 이동하는지 알아보자.

 

7계층은 응용계층이라 불린다.

현재 보고 있는 웹 어플리케이션 같은, 사용자가 접근할 수 있는 가장 가까운 계층이다.

여기서 데이터 전송 등의 명령을 내린다.

 

대표 프로토콜로는 HTTP, SMTP 등이 있는데,

프로토콜이란 데이터를 보내기 위해 정해놓은 통신 규약이다.

우리가 보고있는 웹사이트 화면의 경우 HTTP 프로토콜을 사용한다.

SMTP는 인터넷 상에서 전자메일을 보낼 때 사용되는 프로토콜이다.

 

6계층은 표현 계층이라고 불리며,

응용 계층에서 내린 명령 또는 발송한 데이터를 어떻게 표현할 지 정해주는 계층이다.

즉, 인코딩을 통해 오류 없이 데이터를 주고 받을 수 있도록 표현해주는 계층이다.

 

그림에서 JACK_DATA라는 데이터가 들어온 것을 볼 수 있다.

이 데이터가 변환 및 압축과정을 거쳐 최종적으로 암호화되는 과정을 인코딩이라고 한다.

그리고 이 인코딩의 반대 과정을 디코딩이라고 한다.

 

응용 계층에서 표현 계층까지의 데이터 흐름을 조금 더 쉽게 설명해보자.

 

우리가 편지를 보낼 때 가장 먼저 하는 일이 무엇일까?

바로 편지지를 선택하고 편지 내용을 작성하는 것이 아닐까?

이게 바로 응용 계층에서 하는 일이다.

편지지 선택 = 어플리케이션 선택

편지 내용 작성 = 어플리케이션 실행 및 활동

 

이제 이 편지를 외국인 친구에게 보낸다고 가정해보자.

편지를 받은 친구도 이해할 수 있도록 그 나라 언어로 번역을 하고 편지지를 잘 접어 봉투에 넣는다.

편지를 나와 친구만 알아볼 수 있도록 암호화를 할 수도 있다.

이것이 바로 표현 계층에서 일어나는 일(변환, 압축, 암호화)이다.

 

이제 편지(데이터)를 보낼 준비가 다 되었으니 5계층으로 넘어가보자.

세션 계층은 네트워크상 양쪽 연결을 관리하고 연결을 지속시켜주는 계층이다.

쉽게 말하자면 통신을 함에 있어서 대문 역할을 한다.

 

그리고 이 계층에는 세션 종료, 유지, 중단 시 복구의 기능이 있다.

 

이쯤에서 세션이 무엇인지 잠깐 설명해보면,

방문자가 웹 서버에 접속해 있는 상태를 하나의 단위로 보고 이를 세션이라고 한다.

현재 웹 서버에 접속해 있다면 세션이 연결된 상태이다.

세션에 대해 더 자세한 내용은 다음에 다루기로 한다.

세션 계층은 데이터가 오고 가는 흐름 내에서 동기화 지점을 제공하기도 한다.

동기화 지점은 쉽게 말하면 체크 포인트이다.

다음 예제에서 좀 더 자세히 알아보자.

 

5MB마다 체크포인트가 설정 되어있는 컴퓨터에서 서버로 50MB의 데이터를 전송한다.

 

33MB 정도 전송했는데 이게 무슨 일일까? 전송이 끊겨 버렸다.

이 때 전송되고 있던 데이터는 어떻게 되었을까? 다시 처음부터 전송해야 할까?

 

다행히 세션 계층이 체크포인트를 지정해주었기 때문에 우리는 데이터를 처음부터 전송하지 않아도 된다.

 

이것이 세션 계층의 중요한 기능 중 하나이다.

 

이제 데이터는 전송 계층으로 내려온다.

우리는 앞서 7계층에서 우리가 사용하고 있는 애플리케이션들이 응용 계층이라고 배웠다.

데이터의 여행은 최종적으로 상대 컴퓨터의 7계층에 도달하는 것이다.

 

그럼 지금, 우리의 컴퓨터나 핸드폰을 확인해보자.

달거북씨처럼 10개 이상의 애플리케이션이 실행중일 수도 있고 수십 개가 깔려 있을 수도 있다.

이처럼 수두룩 빽빽한 애플리케이션의 숲에서,

데이터는 어떻게 자신이 가야할 곳을 정확히 찾아갈 수 있을까?

 

그건 바로 데이터가 가지고 있는 일종의 지도, 바로 포트번호 덕분이다.

전송 계층에서는 데이터가 상대 응용 계층을 정확하게 찾아갈 수 있도록 포트번호를 데이터에 달아준다.

데이터는 목적지의 상대 컴퓨터에서 이 포트번호를 확인하고 자신이 가야 할 애플리케이션을 찾아간다.

 

위의 그림을 보자.

데이터의 포트번호는 5223이다.

이 데이터는 어떤 애플리케이션으로 가게 될까?

맞다. 같은 포트번호를 가진 카카오톡을 찾아가게 된다.

 

즉, 포트번호는 보내는 이, 받는 이에게 할당되는 번호로써,

같은 주소 내에서 정확히 받는 사람에게 전달되도록 하는 지표이다.

 

포트번호는 6만 개 이상의 번호가 있고, 번호에 따라 웰노운 포트, 등록된 포트, 동적포트로 나눠진다.

강제는 아니지만 전 세계 모든 컴퓨터가 위의 표와 같은 분류에 따른 포트번호를 사용하도록 권고되고 있다.

 

 

전송계층의 또 다른 역할은 패킷 전송 제어 역할이 있다.

패킷이 전송 과정에서 아무 문제 없이 제대로 수신지의 컴퓨터에 도착할 수 있도록 패킷 전송을 제어하는 것이다.

핵심 프로토콜로는 TCP와 UDP가 있다.

 

패킷이란, 인터넷 내에서 데이터를 보내기 위한 경로배정(라우팅)을 효율적으로 하기 위해 데이터를 여러 개의 조각들로 나누어 전송을 하는데, 이 조각을 패킷이라고 한다.

 

 

TCP와 UDP는 택배에 비유하면 쉽게 이해가 가능하다.

 

쿠팡처럼 수신지에 그냥 물건을 두고 가는 것이 UDP이고,

택배를 전달할 때 수신지에 받는 사람이 있는지 물건이 맞는지 등 정보를 확인하는 과정을 거쳐 전달하는 것이 TCP이다.

 

, TCP는 통신할 컴퓨터끼리보냈습니다’, ‘도착했습니다라고 서로 확인 메시지를 보내면서 여러 번의 확인 과정(3-way-handshake)을 거쳐 데이터를 정확하게 전달한다. 그래서 UDP보다는 속도가 느리다. 반대로 UDP는 데이터를 보내면 그것으로 끝이므로 데이터를 보다 빠르게 전달할 수 있지만 그 만큼 신뢰성은 보장할 수 없다. 

 

 

전송계층이 세 번째 역할은 '세그멘테이션'이다. 단어 뜻 그대로 분할하는 역할을 한다.

 

예를 들어 우리가 수산 시장에서 참치를 구매했다고 가정해보자.

참치는 너무 커서 한 번에 운반하기가 어렵다.

따라서 우리는 참치를 쉽고 편리하게 가져가기 위해서 분할을 해야한다.

이 과정을 세그멘테이션이라고 한다.

 

 

그리고 나눈 것들을 세그먼트라고 한다.

 

 

그럼 왜 세그멘테이션을 해야 할까?

 

예를 들어 전달 받은 상위 계층 데이터를 그대로 전달한다고 해보자.

100MB 동영상을 재생하기 위해서 데이터를 통째로 전달할 경우, 데이터를 모두 전달받아야 재생이 가능하다.

 

반면 세그멘테이션을 통해 패킷을 분할해서 전달하면 어떻게 될까?

데이터를 모두 받을 때까지 기다리지 않아도 전달받은 데이터부터 우선 재생이 가능해진다.

즉, 다운로드 받으면서도 우리는 영상을 볼 수 있게 된다.

 

 

세그멘테이션을 해서 얻는 또 다른 장점은 데이터 전송이 중간에 끊겼을 때 데이터 손실률이 적다는 점이다.

 

데이터를 통으로 전달하다가 전송이 끊기면 데이터가 통째로 손실되지만,

분할되어 있는 데이터는 일부만 손실되기 때문에 손실률이 적다.

 

 

지금까지 7계층에서 4계층까지 내려오는 동안 우리는 데이터가 어떻게 가공되고 어떻게 가는지에 대해 알아봤다. 

3계층부터는 실질적으로 데이터가 어떻게 전달되는지를 공부할 수 있다.

 

네트워크 계층은 실질적으로 보내고 싶은 곳에 데이터를 전달하는 역할을 한다.

 

우선 라우팅을 한다.

라우팅은 쉽게 말하면 데이터를 보낼 최적의 경로를 찾는 것이라고 할 수 있다.

예를 들어, 우리가 어떤 목적지를 갈 때 내비로 최적의 길을 찾아가듯이, 3계층에선 라우터가 그 역할을 한다.

 

 

위 그림처럼 내 컴퓨터에서 상대방의 컴퓨터까지 최적의 경로를 찾는 모습을 볼 수 있다.

 

 

그리고 그 경로를 따라 데이터를 보내는 것을 포워딩이라고 한다.

 

 

이 때, 라우팅과 포워딩을 하는 라우터는 컴퓨터의 IP 주소를 이용해 데이터를 전달한다.

IP주소란, 간단히 설명하면 컴퓨터 네트워크 상에서 서로를 인식하고 통신하기 위해 사용되는 특수한 번호이다.

 

 

지금까지 많은 단계를 거쳐 마침내 데이터를 보낼 '장소' 즉, 컴퓨터가 어디에 있는지까지 파악했다.

하지만 위치만 파악했을 뿐 정확한 상세주소를 모르는 상태이다.

 

데이터링크계층에서는 바로 이 상세주소를 MAC 주소를 통해 파악한다.

 

 

MAC 주소란 일종의 컴퓨터 고유의 번지수이다.

우리집에 모두 각자 주소가 있듯이 컴퓨터 역시 본인만이 가지고 있는 주소가 있고,

이 주소를 2계층에서 분석하여 데이터가 최종적으로 어느 곳으로 가는지 파악한다.

 

 

2계층은 데이터를 보낼 때 MAC 주소를 데이터에 붙여 데이터의 출처를 다른 PC에서도 알 수 있도록 한다.

그리고 그 데이터는 스위치라는 2계층 통신장비에 의해 분석된다.

 

 

2, 3계층의 흐름을 같이 설명하면 이해하기가 쉽다.

 

 A 컴퓨터에서 B 컴퓨터로 데이터를 보내려고 한다.

이 데이터에는 A 컴퓨터와 B 컴퓨터의 IP주소가 모두 적혀 있지만,

도착지의 MAC 주소가 아닌 가장 가까운 라우터의 MAC 주소가 도착지로 적혀있다. 

 

따라서 A에서 스위치로 데이터를 보내면,

스위치에서는 MAC 주소를 살펴보고 해당하는 가장 가까운 라우터로 데이터를 보낸다.

 

라우터는 데이터 도착지에 대한 IP 주소를 체크하여 라우팅 테이블을 통해 라우팅을 시키고,

데이터는 B 컴퓨터 네트워크 근처의 라우터에 도착하게 된다.

 

B 컴퓨터 근처의 라우터는 MAC 주소 테이블을 통해 해당 데이터의 도착지 MAC 주소를 파악하고,

MAC 주소를 도착지의 MAC 주소로 업데이트 하여 스위치로 다시 데이터를 전달한다.

 

데이터를 전달받은 스위치는 MAC 주소를 파악하여 B 컴퓨터로 데이터를 정확하게 보낸다.

 

 

OSI 7계층의 마지막, 1계층은 물리계층이라 불리며 최종적으로 데이터가 도착하는 계층이다.

1계층은 도착한 데이터를 컴퓨터가 이해할 수 있는 이진수의 전자신호로 바뀌고,

이 전기 신호를 다시 아날로그 신호로 바꿔줌으로써 케이블로 이동할 수 있도록 한다.

 

 

전체적인 흐름을 짚어보자.

7계층에서 1계층까지 각 계층마다 헤더를 붙여 1계층까지 내려준다. (헤더는 각 계층별로 처리한 데이터의 결과물이다)

 

 

다른 컴퓨터에 도착한 데이터는 각 계층마다 하나씩 까져서 최종적으로 애플리케이션에 도착하게 된다.

 

이것이 바로 카톡이나 메세지 등을 보낼 때 데이터가 전송되는 과정이다. 

 

 


 

아니,,, 이게 뭐라구 이렇게 정리하는데 오래걸렸을까,,,,,,,

728x90

'이론 > 개인공부' 카테고리의 다른 글

함수형 인터페이스  (0) 2023.01.31
람다식  (0) 2023.01.31
자바 스트림(Stream)  (0) 2022.12.30
정보처리기사 필기  (0) 2022.04.25
220419 STACK & QUEUE  (0) 2022.04.20

1. JDBC 이용해서 회원정보 저장하고 불러오기

2022.07.22 - [이론/자바 풀스택 국비수업] - 0712 Spring Boot 2

 

0712 Spring Boot 2

1. Controller와 Service MVC : Model, View, Controller Spring Framework MVC : Model(DAO, DTO, Service), View, Controller 1-1. 서비스란? 서비스레이어(Service Layer) 단에서 세분화된 비즈니스 로직을 처..

turtlemoon.tistory.com

Spring Boot 2에서는 DB없이 회원정보를 저장하고 불러오는 로직을 짰다면, 이번 포스팅에서는 DB에 저장하고 불러오는 로직을 짜려고 한다.

 

 

1-1. application.properties 설정하기

- dbms 설정을 application.properties에 추가해준다.

#dbms
spring.datasource.url=jdbc:oracle:thin:@localhost:1521:XE
spring.datasource.driver-class-name=oracle.jdbc.OracleDriver
spring.datasource.username=springboot
spring.datasource.password=springboot

 

 

1-2. build.gradle 설정하기

- dependencies{} 안에 사용하는 jdbc 라이브러리를 추가해준다.

- 필요한 라이브러리는 https://mvnrepository.com/에서 검색하여 복붙해주면 된다.

- 추가 후 'Refresh Gradle Project'를 꼭 해주어야 한다.(프로젝트 우클릭 > Gradle > Refresh Gradle Project)

implementation 'org.springframework.boot:spring-boot-starter-jdbc'
implementation group: 'com.oracle.database.jdbc', name: 'ojdbc6', version: '11.2.0.4'

 

 

1-3. JdbcMemberRepository 만들기

- MemoryMemberRepository 대신 사용할 JdbcMemberRepository를 만들어준다.

- MemoryMemberRepository에 설정되어 있는 @Repository는 주석처리해준다.

package com.koreait.core.member.repository;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import javax.sql.DataSource;

import org.springframework.stereotype.Repository;

import com.koreait.core.member.dto.Member;

@Repository
public class JdbcMemberRepository implements MemberRepository{
	
	private final DataSource dataSource;
	public JdbcMemberRepository(DataSource dataSource) {
		this.dataSource = dataSource;
	}

	@Override
	public Member save(Member member) {
		String sql = "INSERT INTO MEMBER values (member_seq.nextval, ?)";
		
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		
		try {
			conn = dataSource.getConnection();
			String generatedColumns[] = {"ID"};
			pstmt = conn.prepareStatement(sql, generatedColumns);
			pstmt.setString(1, member.getName());
			pstmt.executeUpdate();
			rs = pstmt.getGeneratedKeys();
			
			if( rs.next() ) {
				member.setId(rs.getInt(1));
			} else {
				System.out.println("id 조회 실패");
			}
			
		} catch (Exception e) {
			e.printStackTrace();
		} finally {
			try {
				rs.close();
				pstmt.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		return member;
	}

	@Override
	public List<Member> findAll() {
		String sql = "select * from member";
		
		Connection conn = null;
		PreparedStatement pstmt = null;
		ResultSet rs = null;
		List<Member> members = new ArrayList<Member>();
		
		try {
			conn = dataSource.getConnection();
			pstmt = conn.prepareStatement(sql);
			rs = pstmt.executeQuery();
			
			while(rs.next()) {
				Member member = new Member();
				member.setId(rs.getInt("id"));
				member.setName(rs.getString("name"));
				members.add(member);
			}
			
			
		} catch(Exception e) {
			e.printStackTrace();
		} finally {
			try {
				rs.close();
				pstmt.close();
				conn.close();
			} catch (SQLException e) {
				e.printStackTrace();
			}
		}
		
		return members;
	}

}

▶ MemoryMemberRepository에서는 static 메모리를 사용했었고, JdbcMemberRepository에서는 DataSource 사용한다.

▶ sql 연결과 쿼리문은 jsp 때 했던 것처럼 하면 된다.

 

DataSource
순수 jdbc로 데이터베이스에 접근할 경우,

데이터베이스에 접근할 때마다 커넥션을 맺고 끊는 작업을 한다.
이 작업을 줄이기 위해 미리 커넥션을 생성해 두고,
데이터베이스에 접근하고자 하는 사용자에게 미리 생성된 커넥션을 제공하고 돌려받는다.
이 커넥션들을 모아두는 장소를 Connection Pool이라 하며,
DataSource는 Java에서 Connection Pool을 지원하기 위한 인터페이스이다.

 

 

1-4. 시연

- 나머지 컨트롤러와 뷰단은 동일하게 사용 가능하다.

시퀀스 이상한 거는 무시해주세요:D

 

개방-폐쇄 원칙(OCP, Open-Closed Principle)
확장에는 열려 있고, 수정 및 변경에는 닫혀 있는 원칙이다.

스프링의 DI(Dependencies Injection)을 이용하면,
기존 코드를 전혀 손대지 않고 설정만으로 구현 클래스를 변경 할 수 있다. 

 

 


 

 

2. JPA 이용해서 회원정보 저장하고 불러오기

2-1. JPA(Java Persistence API)

  • JPA는 자바 진영에서 ORM(Object-Relational Mapping) 기술 표준으로 사용되는 인터페이스의 모음이다. 즉, 실제로 구현된 것이 아니라 구현된 클래스와 매핑을 해주기 위해 사용되는 프레임워크이다.
  • 대표적인 오픈소스로는 Hibernate가 있다.(JPA는 표준 인터페이스, Hibernate는 구현체)

  • JPA는 애플리케이션과 JDBC 사이에서 동작한다.
  • 기존의 반복 코든느 물론이고, 기본적인 SQL도 JPA가 직접 만들어서 실행해준다.
  • 따라서 개발자는 SQL과 데이터 중심의 설계에서 객체 중심의 설계로 패러다임을 전환할 수 있고, 개발 생산성을 크게 높일 수 있다.

 

 

ORM(Object-Relational Mapping)
애플리케이션 Class와 RDB(Relational DataBase)의 테이블을 매핑(연결)한다는 뜻으로,
기술적으로는 애플리케이션의 객체를 RDB 테이블에 자동으로 영속화 해주는 것이라고 보면 된다.
한 마디로 객체와 RDB를 자동 매핑(연결)하는 기능!

객체지향 프로그래밍은 클래스를 사용하고, 관계형 데이터베이스는 테이블을 사용한다.
따라서 객체 모델과 관계형 모델 간의 불일치가 존재한다.
이 때, ORM을 통해 객체 간의 관계를 바탕으로 SQL을 자동으로 생성하여 불일치를 해결한다.
객체를 통해 간접적으로 데이터베이스 데이터를 다룬다.

 

 

2-2. application.properties 설정하기

- jpa 설정을 application.properties에 추가해준다.

#jpa
spring.jpa.show-sql=true
spring.jpa.hibernate.ddl-auto=none

 

 

2-3. build.gradle 설정하기

- dependencies{} 안에 jpa 라이브러리를 추가해준다.

- 'Refresh Gradle Project' 잊지 말기!

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'

 

 

2-4. Member DTO 수정하기

- JPA가 관리하는 클래스에는 @Entity를 붙여주어야 한다.

package com.koreait.core.member.dto;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.SequenceGenerator;

 @Entity
public class Member {
	// @Id : 기본키 매핑
	@Id 
	// 시퀀스 사용을 위한 어노테이션
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "mySequenceGenerator")
	@SequenceGenerator(name = "mySequenceGenerator", sequenceName = "member_seq", allocationSize = 1)
	
	private int id;
	private String name;
	
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

 

 

2-5. JpaMemberRepository 만들기

- @Repository를 붙여준다.

- MemoryMemberRepository, JdbcMemberRepository 모두 @Repository에 주석처리가 되어 있어야 한다.

package com.koreait.core.member.repository;

import java.util.List;
import javax.persistence.EntityManager;
import org.springframework.stereotype.Repository;
import com.koreait.core.member.dto.Member;

@Repository
public class JpaMemberRepository implements MemberRepository {

	private final EntityManager em;
	public JpaMemberRepository(EntityManager em) {
		this.em = em;
	}
	
	
	@Override
	public Member save(Member member) {
		em.persist(member);
		return member;
	}

	@Override
	public List<Member> findAll() {
		return em.createQuery("select m from Member m", Member.class).getResultList();
	}
}

▶ JPA를 사용하려면 EntityManaager를 주입받아야 한다.

▶ select m from Member m은 JPQL이라는 쿼리이다.

▶ Member라는 Entity를 조회해 select 할 때, Member라는 객체 자체를 select 한다.

 

JPQL(Java Persisstence Query Language)
테이블이 아닌 엔티티 객체를 대상으로 검색하는 객체 지향 쿼리

(▶ 엔티티 객체 설명 참고 : https://whitepro.tistory.com/265)
SQL을 추상화하기 때문에 특정 데이터베이스 SQL에 의존하지 않는다.
JPA는 JPQL을 분석한 후 적절한 SQL을 만들어 데이터베이스를 조회한다.

대소문자 구분의 경우,
JPQL 키워드는 구분하지 않지만 엔티티와 속성은 대소문자를 구분한다.

별칭은 필수이다.
JPQL은 별칭이 필수이고, AS는 생략 가능하다.

 

 

2-6. MemberService 수정

- @Transactional을 추가해준다.

▶ 강사님께서 자세한 설명을 해주지 않아 찾아봤는데 끙 어렵다...

 

 


 

 

3. SOLID 원칙

클린코드를 위한 좋은 객체지향 설계의 5가지 원칙

  • SRP : 단일 책임 원칙(Single Responsibility Principle)
  • OCP : 개방-폐쇄 원칙(Open/Closed Principle)
  • LSP : 리스코프 치환 원칙(Liskov Subsitution Principle)
  • ISP : 인터페이스 분리 원칙(Interface Segregation Principle)
  • DIP : 의존관계 역전 원칙(Dependency Inversion Principle)

 

 

3-1. SRP : 단일 책임 원칙(Single Responsibility Principle)

  • 하나의 클래스는 하나의 책임을 가져야 한다.
  • 하지만 '하나의 책임'이라는 것은 모호하다. 클 수도 있고, 작을 수도 있으며, 문맥과 상황에 따라 다르다.
  • 중요한 기준은 변경이다. 변경이 있을 때 파급 효과가 적으면 단일 책임 원칙을 잘 따른 것 

 

 

3-2. OCP : 개방-폐쇄 원칙(Open-Closed Principle)

  • 확장에는 열려 있고, 수정 및 변경에는 닫혀 있는 원칙이다.
  • 스프링의 DI(Dependencies Injection)을 이용하면, 기존 코드를 전혀 손대지 않고 설정만으로 구현 클래스를 변경 할 수 있다.

 

 

3-3. LSP : 리스코프 치환 원칙(Liskov Substitution Principle)

  • 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.
  • 다형성을 지원하기 위한 원칙으로, 다형성에서 하위 클래스는 인터페이스 규약을 다 지켜야 한다. 이는 인터페이스를 구현한 구현체를 믿고 사용하기 위해 필요한 약속이다.
  • 단순히 컴파일에 성공하는 것을 일컫지 않는다.
  • 예를 들어 자동차라는 인터페이스 안에 엑셀이라는 메서드가 있을 때, 자식 클래스에서 이 엑셀 메서드에 뒤로 가는 기능을 오버라이드 한다면, 컴파일에는 성공할 지라도 엑셀의 의미를 잃기 때문에 리스코프 치환 원칙에 위배된다. 

 

 

3-4. ISP : 인터페이스 분리 원칙(Interface Segregation Principle)

  • 특정 클라이언트를 위한 인터페이스 여러 개가 범용 인터페이스 하나보다 낫다.
  • 인터페이스가 명확해지고, 대체 가능성이 높아진다. 즉, 대체 시 변화가 최소한으로 이루어진다.

 

 

3-5. DIP : 의존관계 역전 원칙(Dependency Inversion Principle)

  • 프로그래머는 추상화에 의존해야지, 구체화에 의존하면 안된다.
  • 의존성 주입은 이 원칙을 따르는 방법 중 하나다.
  • 쉽게 말해서 구현 클래스에 의존하지 말고, 인터페이스에 의존하라는 뜻이다.
  • 인터페이스에 의존해야 유연하게 구현체를 변경할 수 있다. 만약 구현체에 의존하면 변경이 아주 어려워진다.
728x90

'이론 > 자바 풀스택 국비수업' 카테고리의 다른 글

0712 Spring Boot 2  (0) 2022.07.22
0706 SpringBoot 1  (0) 2022.07.18
220523 jQuery 5  (0) 2022.07.17
220519 jQuery 4  (0) 2022.07.13
220519 jQuery 3  (0) 2022.07.07

1. Controller와 Service

MVC : Model, View, Controller

Spring Framework MVC : Model(DAO, DTO, Service), View, Controller

 

 

1-1. 서비스란?

  • 서비스레이어(Service Layer) 단에서 세분화된 비즈니스 로직을 처리하는 객체이다.
  • 서비스는 비즈니스 로직이 들어가는 부분이다.
  • Controller가 Request를 받으면 적절한 Service에 전달하고, 전달된 Service는 비지니스 로직을 처리한다.
  • 각 데이터는 DTO로 데이터를 전달받고, DAO로 데이터베이스를 접근한다.

 

1-2. Controller와 Service

  • Controller가 Service를 의존한다고 표현한다.
  • Service는 여러 컨트롤러에서 가져다 사용 가능하다.

 

 


 

 

2. DB없이 회원정보 저장하고 불러오기

어떤 데이터베이스를 사용할 지 정해지지 않았을 때, 우선 개발부터 시작할 수 있는 방법

 

 

2-1. Member DTO(VO) 만들기

회원정보를 받아오는 Member DTO(VO)

package com.koreait.test.member.dto;

public class Member {
	// 변수
	private int id;
	private String name;
	
	// getter, setter
	public int getId() {
		return id;
	}
	public void setId(int id) {
		this.id = id;
	}
	public String getName() {
		return name;
	}
	public void setName(String name) {
		this.name = name;
	}
}

 

 

2-2. Repository 생성

DB 대신 사용할 인터페이스를 만들어준다.

package com.koreait.test.member.repository;

import java.util.List;
import com.koreait.test.member.dto.Member;

// DB 대신 사용
public interface MemberRepository {
	// 회원 저장
	Member save(Member member);
	// 회원 찾기
	List<Member> findAll();
}

 

 

2-3. 임시저장용 클래스 생성

DB가 없기 때문에 메모리에 임시저장한다.

package com.koreait.test.member.repository;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import com.koreait.test.member.dto.Member;

@Repository
public class MemoryMemberRepository implements MemberRepository {

	// 메모리 사용 - static
	private static Map<Integer, Member> store = new HashMap<Integer, Member>();	// DB처럼 사용
	private static int sequence = 0;	// id에 해당
	
	@Override
	public Member save(Member member) {
		member.setId(++sequence);
		store.put(member.getId(), member);
		return member;
	}

	@Override
	public List<Member> findAll() {
		return new ArrayList<Member>(store.values());
	}
	
}

 

 

2-4. Service 생성

package com.koreait.test.member.service;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.koreait.test.member.dto.Member;
import com.koreait.test.member.repository.MemberRepository;
import com.koreait.test.member.repository.MemoryMemberRepository;

/*
* MemberService는 순수한 java Class(=POJO)라서 스프링이 인식하지 못한다
* 따라서 스프링이 찾을 수 있도록 적절한 어노테이션(@Service)을 선언해주어야 한다.
*/
@Service
public class MemberService {
	// 오른쪽은 인터페이스, 왼쪽은 실제 클래스
	MemberRepository memberRepository = new MemoryMemberRepository();
	
	// 생성자 주입
	@Autowired
	public MemberService(MemberRepository memberRepository) {
		this.memberRepository = memberRepository;
	}
	
	// 회원가입
	public int join(Member member) {
		memberRepository.save(member);
		return member.getId();
	}
	
	// 전체 회원 조회
	public List<Member> findMembers(){
		return memberRepository.findAll();
	}
}

 

POJO(Plain Old Java Object)
단순한 자바 오브젝트
객체 지향적인 원리에 충실하면서 환경과 기술에는 종속되지 않고
필요에 따라 재활용 될 수 있는 방식으로 설계된 오브젝트를 말한다.

POJO에 어플리케이션의 핵심 로직과 기능을 담아 설계하고 개발하는 방법을 POJO 프로그래밍이라고 한다.

 

 

2-5. Controller 생성

package com.koreait.test.member.controller;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

import com.koreait.test.member.dto.Member;
import com.koreait.test.member.service.MemberService;

@Controller
public class MemberController {
	// 기존 방식 : MemberService mService = new MemberService();
	// Spring 방식 :
	private final MemberService memberService;	
	// 선언만 해주면 할당은 스프링 프레임워크가 알아서 해준다. -> IOC(Inversion of Control)
    
	// 생성자 호출
	// MemberController가 생성될 때, 생성자를 호출해준다. 
   	// 즉, service까지 생성해서 자동으로 호출해준다.
    	// @Autowired를 선언해주면, MemberController를 생성하면서 스프링이 memberService와 연결해준다.
	// 단일 생성자에 한해 @Autowired 생략이 가능하다.	
    @Autowired
	public MemberController(MemberService memberService) {
		this.memberService = memberService;
	}
}

 

IoC(Inversion of Control)
개발자가 프레임워크의 기능을 호출하지 않고 프레임워크가 개발자의 코드를 호출한다.
때문에 개발자는 전체를 직접 구현하지 않고 부분적으로 코드를 "끼워넣는" 형태로 구현할 수 있다.

프레임워크가 객체의 라이프 사이클을 관리하며, 스프링으로부터 필요한 객체를 얻어올 수 있다.
객체의 의존성을 역전시켜 객체 간의 결합도를 줄인다. 또한 유연한 코드를 작성할 수 있게 하여 가독성 향상, 코드 중복 방지, 유지 보수 편리 등의 장점을 갖는다.

 

스프링 컨테이너
스프링은 실행 시 객체들을 담고 있는 컨테이너가 있고,
런타임 과정에서 이 컨테이너가 객체들 간의 의존관계를 알아서 만들어 준다.
스프링 컨테이너는 자바 객체의 생명주기를 관리하며,
생성된 자바 객체들에게 추가적인 기능을 제공하는 역할을 한다.
스프링에서는 이 자바 객체를 빈(Bean)이라고 부른다.
개발자는 객체를 생성하고 소멸할 수 있는데, 
스프링 컨테이너가 이 역할을 대신함으로써 제어의 흐름을 외부에서 관리하게 된다.

즉, 스프링 컨테이너에 객체들을 생성해 보관하고 있고,
그 관리를 스프링이 가져간 것을 Ioc(제어의 역전)이라고 한다.

 

스프링 빈 등록 방법
1. 컴포넌트 스캔 : @Component > 해당 어노테이션이 붙은 클래스를 스프링이 모두 스캔한다.

2. 자바 코드로 직접 스프링 빈을 등록하는 방법(거의 사용하지 않는다)

@Component를 붙여주지 않았는데도 빈이 등록된 이유는,
우리가 붙여준 @Controller, @Service, @Repository 등에 @Componenet가 포함되어 있기 때문이다.

참고로 스프링은 스프링 컨테이너에 빈을 등록할 때, 기본적으로 싱글톤으로 등록한다.
따라서 같은 스프링 빈이면 모두 같은 인스턴스이다.

 

@Autowired
필요한 의존 객체의 "타입"에 해당하는 빈을 찾아 주입한다.

의존성 주입을 할 대상을 찾지 못하면 애플리케이션 구동에 실패한다.

 

클래스 객체마다 역할에 맞는 어노테이션을 표시해주지 않으면
스프링이 찾지 못해 제대로 구동되지 않는다.

 

DI(Dependency Injection) : 의존성 주입
객체 간에 의존성이 존재할 경우, 개발자가 직접 객체를 생성하거나 제어하는 것이 아니라,

제어반전에 의하여 특정 객체에 필요한 다른 객체를 프레임워크가 자동으로 연결시켜주는 것을 말한다.
개발자는 객체를 선언만 할 뿐, 할당은 프레임워크에 의해서 자동으로 이루어진다.
따라서 개발자는 비지니스 로직 개발에만 집중할 수 있다.

 

DI의 3가지 방법
1. Field Injection(필드 주입)

2. Setter Injection(수정자 주입)
3. Constructure Injection(생성자 주입) < 우리가 사용하는 방법
// DI 1번 방법. Field Injection
@Autowired MemberService memberService;

// DI 2번 방법. Setter Injection
@Autowired
public void setMemberService(MemberService memberService) {
	this.memberService = memberService;
}

// DI 3번 방법. Constructor Injection
@Autowired
public MemberController(MemberService memberService) {
    this.memberService = memberService;
}

 

Private를 붙이는 이유
application 로딩 시점에 조립하기 때문에 중간에 바꿀 일이 없다.

따라서 private를 붙여 중간에 바뀔 가능성을 닫아놓는다.

final을 붙이는 이유
선언된 레퍼런스 타입 변수는 반드시 선언과 함께 초기화가 되어야 한다.
객체가 불변하도록 하기 때문에, 누군가가 Controller 내부에서 Service 객체를 바꿔치기 할 수 없다.

 

 

2-6. 홈컨트롤러 생성

package com.koreait.test.member.controller;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;

@Controller
public class HomeController {
	// 기본 localhost:9090으로 요청이 들어오면 home.html을 호출한다.
	@GetMapping("/")
	public String home() {
		return "home";
	} 
}

▶ 스프링부트 기본 웰컴페이지는 static 아래에 있는 index.html로 연결된다.

▶ @GetMapping("/")을 통해 웰컴페이지를 "home"으로 바로 연결해준다.

 

 

2-7. home.html 생성

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div>
		<h1>Spring Boot</h1>
		<p>회원관리</p>
		<p>
			<a href="/members/new">회원가입</a>
			<a href="/members">회원목록</a>
		</p>
	</div>
</body>
</html>

 

 

2-7. 회원가입 경로 만들기

위에서 만든 MemberController에 경로를 만들어준다.

@GetMapping(value = "/members/new")
	public String createForm() {
		return "members/createMemberForm";
	}

▶ /members/new로 주소가 들어오면 createForm 메서드가 받아 createMemberForm.html로 연결해준다.

 

 

2-8. 회원가입 페이지 만들기

- createMemberForm.html

<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div>
		<form action="/members/new" method="post">
			<div>
				<label for="name">이름</label>
				<input type="text" id="name" name="name" placeholder="이름을 입력하세요">
			</div>
			<button type="submit">등록</button>
		</form>
	</div>
</body>
</html>

▶ DTO에서 id는 시퀀스로 만들어주도록 설정해 놓았기 때문에, 회원가입 페이지에서는 이름만 받아오면 된다.

 

 

2-9. 이름만 받는 DTO 만들기

- 기존 DTO를 사용할 수도 있지만 새로 만듦

▶ 왤까...? 필요한 변수만 사용하기 위해서일까? 이유를 설명해주지 않으셔서 잘 모르겠음

package com.koreait.core.member.dto;

public class MemberForm {
	private String name;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}
}

 

 

2-10. 회원가입 처리

- MemberForm으로 받은 이름 Member로 보내 저장하기

- MemberController에서 경로를 만들어준다.

// 회원가입
@PostMapping(value = "/members/new")
public String create(MemberForm form) {
    Member member = new Member();
    member.setName(form.getName());

    memberService.join(member);

    // 홈화면으로 돌린다 -> redirect
    return "redirect:/";		
}

▶ home.html과 createMemberForm.html 두 곳에 각각 /members/new 경로로 보내지만 충돌이 생기지는 않는다.

▶ home.html에서는 Get으로, createMemeberForm.html에서는 Post 방식으로 경로가 들어오기 때문이다. 

▶ MemberForm에서 받은 이름을 Member로 보내주면 MemberService의 join메서드가 알아서 처리해준다.

 

 

2-11. 회원목록 받아오기

- Member DTO에서 회원목록을 받아 회원목록 페이지로 보내주는 경로를 MemberController에 만든다.

@GetMapping("/members")
public String list(Model model) {
    List<Member> members = memberService.findMembers();
    model.addAttribute("members", members);
    return "members/memberList";
}

▶ MemberService에 만든 findMembers 메서드로 전체 회원을 조회하여 Model에 저장해서 전달한다.

 

 

2-12. 회원목록 페이지 만들기

- memberList.html

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<div>
		<table>
			<thead>
				<tr>
					<th>no</th>
					<th>이름</th>
				</tr>
			</thead>
			<tbody>
				<tr th:each="member : ${members }">
					<td th:text="${member.id}"></td>
					<td th:text="${member.name}"></td>
				</tr>
			</tbody>
		</table>
	</div>
</body>
</html>

▶ 테이블 형식으로 받아오도록 html을 짜준다.

▶ th:each는 반복문의 역할을 한다.

▶ 모델에 저장되어 있는 members에서 id와 name을 모두 출력한다. 

 

 

2-13. 시연

 

 

2-14. 구조 정리

728x90

'이론 > 자바 풀스택 국비수업' 카테고리의 다른 글

0713 Spring Boot 3  (0) 2022.07.22
0706 SpringBoot 1  (0) 2022.07.18
220523 jQuery 5  (0) 2022.07.17
220519 jQuery 4  (0) 2022.07.13
220519 jQuery 3  (0) 2022.07.07

1. 이론

1-1. Framework

  • 뼈대나 근간을 이루는 코드들의 묶음
  • 프로그램의 기본 흐름이나 구조를 정하고, 이 구조에 자신의 코드를 추가하는 방식으로 개발하는 프로그래밍 기본 틀
  • 즉, 개발에 필요한 구조가 제공되고, 그 구조에 필요한 부분을 조립하는 형태로 개발이 진행된다.

 

 

1-2. 라이브러리

라이브러리란, 자주 사용되는 로직을 재사용하기 편리하도록 잘 정리한 일련의 코드들의 집합이다.

 

 

1-3. 프레임워크 vs 라이브러리

  • 프레임워크는 자동차 프레임, 즉 기본적으로 구성된 뼈대를 말한다.
  • 라이브러리는 자동차의 기능을 하는 부품을 의미한다.
  • 한 번 정해진 자동차의 프레임은 바꿀 수 없다.
  • 즉, 소형차를 만들기 위한 뼈대로 SUV를 만들 수는 없다.(프레임워크)
  • 그러나 바퀴나 선루프, 헤드라이트 등은 비교적 다른 종류로 쉽게 바꿀 수 있다.(라이브러리)

 

 

1-4. Spring이란? 

스프링 프레임워크는 자바 플랫폼을 위한 오픈소스 애플리케이션 프레임워크로써, 간단히 Spring이라 부른다.

동적인 웹사이트를 개발하기 위한 여러 가지 서비스를 제공하고 있다.

 

 

1-5. Spring Boot

스프링 프레임워크는 기능이 많아 환경설정이 복잡한 편이다. 이를 쉽게 만드는 것이 바로 스프링 부트이다.

  • Embed Tomcat을 사용하기 때문에 따로 Tomcat을 설치하거나 매번 버전 관리해 주어야 하는 수고로움을 덜어준다.
  • starter를 통한 dependency 자동화
    스프링에서는 각각의 dependency들의 호환되는 버전을 일일이 맞추어 주어야 했고, 때문에 하나의 버전을 올리면 다른 dependency에 까지 영향을 미쳐 version 관리에 어려움이 많았다. 하지만 스프링 부트에서는 starter가 대부분의 dependency를 관리해주기 때문에 이러한 걱정을 덜게 되었다.
  • XML 설정을 하지 않아도 된다.
  • jar 파일을 이용해 자바 옵션만으로 손쉽게 배포가 가능하다. 
  • Spring Actuator를 이용한 애플리케이션 모니터링과 관리를 제공한다.

 

 


 

 

2. 환경설정

  • STS3
    - 설치 시 java 11이 필요하기 때문에 다운로드
  • java 8

 

 

2-1. 프로젝트 생성

https://start.spring.io/

  • Project : 사용할 필드 툴 선택(Maven/Gradle)
    - Maven, Gradle은 프로젝트 의존성을 관리하는 빌드 라이프사이클을 관리해주는 툴이다. 
    - 과거에는 Maven을, 최근에는 Gradle을 사용한다.
  • Spring Boot 버전 선택
    - SNAPSHOT이 붙은 것은 현재 개발 중인 버전
    - M(Minor)은 정식 릴리즈 되지 않은 버전
    - 아무것도 붙어있지 않은 것은 정식 릴리즈된 버전. 따라서 정식 릴리즈 된 버전 중 가장 최신 버전을 선택한다.
  • Project Metadata : 정보
    - Group은 기업의 도메인
    - Artifact는 비리드 결과물, 프로젝트 명
    - 위 두 개를 설정하면 패키지 명이 자동으로 설정된다.
  • Packaging
    - JSP 때는 War를 사용했지만, Spring Boot에서는 Jar를 사용한다.
  • Dependencies
    - 기본적으로 Spring Web과 Thymeleaf를 선택해준다.
  • Generate 버튼 혹은 Ctrl+Enter를 하면 현재 설정을 기반으로 한 프로젝트 압축 파일이 다운로드 된다.

 

 

2-2. STS에 프로젝트 불러오기

  • 생성된 파일을 STS 워크스페이스로 불러온 뒤 압축을 풀어준다.
  • STS 실행 후 Package Explorer에서 마우스 우클릭 후 import 클릭

 

  • import 창에서 Gradle 폴더 안 Existing Gradle Project 클릭

 

  • Next > 클릭 후 압축 파일을 풀어준 폴더 선택한 뒤 Finish 클릭

 

  • 프로젝트 생성 시, 톰캣이 내장되어 있기 때문에 서버에 자동으로 추가된다.

 

 

2-3. application.properties

애플리케이션에서 사용하는 여러 가지 설정 값들을 애플리케이션의 밖이나 안에 정의할 수 있는 파일이다.

스프링 부트가 애플리케이션을 구동할 때 자동으로 로딩되는 파일로, key-value 형식으로 값을 정의하면 애플리케이션에서 참조하여 사용할 수 있다.

 

▶ #을 달아 주석처리가 가능하다.

▶ 포트 설정, 인코딩 설정, dbms 연결 등을 하는 곳이다.

▶ 한글이 깨지면 'Properties'에 들어가 UTF-8로 설정해준다. 

 

 

2-4. build.gradle

▶ 프로젝트를 생성하면서 우리가 설정했던 사항이 들어가 있다.

▶ 다른 기능을 추가하고 싶을 때는 dependencies{} 안에 추가해주면 된다.

 

 


 

 

3. Welcome Page

스프링부트에서는 static 폴더 안의 index.html이 로컬주소를 주소창에 넣었을 때 처음으로 뜨는 웰컴페이지이다.

 

[출력 결과]

 

 


 

 

4. DispatcherServlet

  • 스프링 MVC도 프론트 컨트롤러 패턴으로 구현되어 있다.
  • 스프링 MVC의 프론트 컨트롤러가 바로 디스패처 서블릿이다.
  • 그리고 이 디스패처 서블릿이 스프링 MVC의 핵심이다.
  • DispatcherServlet -> FrameworkServlet -> HttpServletBean -> HttpServlet

 

4-1. HTTP 요청 및 응답 흐름

1) 핸들러 조회 : 핸들러 매핑을 통해 요청 URL에 매핑된 핸들러(컨트롤러)를 조회한다.

2) 핸들러 어댑터 조회 : 핸들러를 실행할 수 있는 핸들러 어댑터를 조회한다.

3) 핸들러 어댑터 실행 : 핸들러 어댑터 실행

4) 핸들러 실행 : 핸들러 어댑터가 실제 핸들러를 실행한다.

5) ModelAndView 반환 : 핸들러 어댑터는 핸들러가 반환하는 정보를 ModelAndView로 변환해서 반환한다.

6) ViewResolver 호출 : 뷰 리졸버를 찾고 실행한다.

7) View 반환 : 뷰 리졸버에서 뷰의 논리 이름을 물리 이름으로 바꾸고, 랜더링 역할을 담당하는 뷰 객체를 반환한다.

8) 뷰 렌더링 : 뷰를 통해서 뷰를 렌더링한다.

 

 

파란 부분은 스프링 프레임워크가 알아서 해준다.

 

 


 

 

5. 예제 코드

< index.html 링크 클릭 시 해당 페이지 불러오기 >

 

index.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<a href="/hello">hello</a>
</body>
</html>

 

HelloController.java
@Controller
public class HelloContrller {
	@GetMapping("hello")
	public String hello(Model model) {
		model.addAttribute("data", "new springboot!!");
		return "hello";
	}
  • @Controller
    - Controller 클래스에는 @Controller가 필요(import 필수)
    - Controller에서 해당 mapping URL을 찾아 연결해준다.
  • @GetMapping
    - get 방식의 요청 mapping(import 필수)
  • Model은 일종의 데이터 담는 형식(import 필수, 나중에 더 자세히 배울 예정)
    - .addAttribute("key", "value")를 통해 데이터를 담아 전달할 수 있다.
  • "hello"를 return 시, templates/hello.html로 자동으로 매핑된다.
    즉, 컨트롤러에서 리턴 값으로 문자를 반환하면 뷰리졸버(ViewResolver)가 화면을 찾아서 처리한다.
    스프링 부트는 기본 viewName을 매핑해준다.
    resources : templates/ + {viewName} + .html

 

hello.html
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Insert title here</title>
</head>
<body>
	<p th:text="'/Hello.' + ${data}">Hello Spring Boot</p>
</body>
</html>
  • 타임리프(thymeleaf)를 사용하기 위해서는 <html xmlns:th="http://www.thymeleaf.org">을 html태그에 넣어준다.
  • 타임리프는 th:로 시작한다.
  • ${data}에 HelloControler.java에서 넣어준 data를 가져온다. 출력 시 /Hello. new Springboot!!
  • 위와 같이 코드를 작성함으로써, 프론트단 작업 화면과 개발자 화면을 분리해서(?) 볼 수 있다.

 

http://localhost:9999/hello

타임리프로 작성한 부분이 서버로 구동된다.

 

file:///D:/0900_GB_KMoon/spring/workspace/test/src/main/resources/templates/hello.html

파일을 그대로 열었을 땐 html 작업 부분이 출력된다.

 

728x90

'이론 > 자바 풀스택 국비수업' 카테고리의 다른 글

0713 Spring Boot 3  (0) 2022.07.22
0712 Spring Boot 2  (0) 2022.07.22
220523 jQuery 5  (0) 2022.07.17
220519 jQuery 4  (0) 2022.07.13
220519 jQuery 3  (0) 2022.07.07

+ Recent posts