5주 차에서 다룰 내용은 다음과 같다.
1. Operating System
- 운영체제란?
- 프로세스 vs 스레드
- 프로세스 주소 공간
- Interrupt
2. Spring
- Test Code(JUnit5)
- JPA
- JPA 더티 체킹
- Spring Security - 인증 및 권한 부여
Operating System
운영체제란?
운영 체제는 사용자가 컴퓨터를 편리하고 효과적으로 사용할 수 있도록 환경을 제공하는 시스템 소프트웨어이다.
운영체제의 역할
1. 프로세스 관리
운영체제에서 작동하는 응용 프로그램을 관리하는 기능이다.
현재 CPU를 점유해야 할 프로세스를 결정하고, 실제로 CPU를 프로세스에 할당하며,
이 프로세스 간 공유 자원 접근과 통신 등을 관리하게 된다.
2. 저장장치 관리
1차 저장장치에 해당하는 메인 메모리와 2차 저장장치에 해당하는 하드디스크, NAND 등을 관리하는 기능이다.
- 1차 저장장치(Main Memory)
- 프로세스에 할당하는 메모리 영역의 할당과 해제
- 각 메모리 영역 간의 침범 방지
- 메인 메모리의 효율적 활용을 위한 가상 메모리 기능
- 2차 저장장치(HDD, NAND Flash Memory 등)
- 파일 형식의 데이터 저장
- 이런 파일 데이터 관리를 위한 파일 시스템을 OS에서 관리
- FAT, NTFS, EXT2, JFS, XFS 등 많은 파일 시스템들이 개발되어 사용 중
3. 네트워킹
TCP/IP 기반의 인터넷에 연결하거나, 응용 프로그램이 네트워크를 사용하려면 운영체제에서 네트워크 프로토콜을 지원해야 한다.
현재 상용 OS들은 다양하고 많은 네트워크 프로토콜을 지원한다.
4. 사용자 관리
운영체제는 각 계정을 관리할 수 있는 기능이 필요하다.
사용자 별로 프라이버시와 보안을 위해 개인 파일에 대해선 다른 사용자가 접근할 수 없도록 해야 한다.
이밖에도 파일이나 시스템 자원에 접근 권한을 지정할 수 있도록 지원하는 것이 사용자 관리 기능이다.
5. 디바이스 드라이버
시스템에는 여러 하드웨어가 붙어있는데, 이들을 운영체제에서 인식하고 관리하게 만들어 응용 프로그램이 하드웨어를 사용할 수 있게 만들어야 한다.
운영체제 안에 하드웨어를 추상화해주는 계층이 필요하다.
이 계층이 바로 디바이스 드라이버라고 불린다.
하드웨어의 종류가 많은 만큼, 운영체제 내부의 디바이스 드라이버도 많이 존재한다.
이러한 수많은 디바이스 드라이버들을 관리하는 기능 또한 운영체제가 맡고 있다.
프로세스 VS 스레드
프로세스 : 프로그램을 메모리 상에서 실행 중인 작업
스레드 : 프로세스 안에서 실행되는 여러 흐름 단위
프로세스는 각각 별도의 주소 공간 할당 (독립적)
- Code : 코드 자체를 구성하는 메모리 영역(프로그램 명령)
- Data : 전역 변수, 정적 변수, 배열 등
- 초기화된 데이터는 data 영역에 저장
- 초기화되지 않은 데이터는 bss 영역에 저장
- Heap : 동적 할당 시 사용 (new(), malloc() 등)
- Stack : 지역변수, 매개변수, 리턴 값 (임시 메모리 영역)
프로세스는 자신만의 고유 공간과 자원을 할당받아 사용하는데 반해, 스레드는 다른 스레드와 공간, 자원을 공유하면서 사용하는 차이가 존재함
멀티 프로세스
하나의 프로그램을 여러 개의 프로세스로 구성하여 각 프로세스가 병렬적으로 작업을 수행하는 것
장점 : 안전성 (메모리 침범 문제를 OS 차원에서 해결)
단점 : 각각 독립된 메모리 영역을 갖고 있어, 작업량 많을수록 오버헤드 발생. Context Switching으로 인한 성능 저하
멀티 스레드
하나의 응용 프로그램에서 여러 스레드를 구성해 각 스레드가 하나의 작업을 처리하는 것
장점 : 독립적인 프로세스에 비해 공유 메모리만큼의 시간, 자원 손실이 감소 전역 변수와 정적 변수에 대한 자료 공유 가능
단점 : 안전성 문제. 하나의 스레드가 데이터 공간 망가뜨리면, 모든 스레드가 작동 불능 상태 (공유 메모리를 갖기 때문)
프로세스의 주소 공간
프로그램이 CPU에 의해 실행됨 -> 프로세스가 생성되고 메모리에 프로세스 주소 공간이 할당됨
프로세스 주소 공간에는 코드, 데이터, 스택으로 이루어져 있다.
- 코드 Segment : 프로그램 소스 코드 저장
- 데이터 Segment : 전역 변수 저장
- 스택 Segment : 함수, 지역 변수 저장
왜 이렇게 구역을 나눈 건가요?
최대한 데이터를 공유하여 메모리 사용량을 줄여야 한다.
Code는 같은 프로그램 자체에서는 모두 같은 내용이기 때문에 따로 관리하여 공유한다.
Stack과 data를 나눈 이유는, 스택 구조의 특성과 전역 변수의 활용성을 위한 것이다.
프로그램의 함수와 지역 변수는, LIFO(가장 나중에 들어간 게 먼저 나옴) 특성을 가진 스택에서 실행된다.
따라서 이 함수들 안에서 공통으로 사용하는 '전역 변수'는 따로 지정해주면 메모리를 아낄 수 있다.
인터럽트(Interrupt)
프로그램을 실행하는 도중에 예기치 않은 상황이 발생할 경우 현재 실행 중인 작업을 즉시 중단하고,
발생된 상황에 대한 우선 처리가 필요함을 CPU에게 알리는 것
외부 인터럽트 - 입출력 장치, 타이밍 장치, 전원 등 외부적인 요인으로 발생
내부 인터럽트 - Trap이라고 부르며, 잘못된 명령이나 데이터를 사용할 때 발생
소프트웨어 인터럽트 - 프로그램 처리 중 명령의 요청에 의해 발생한 것 (SVC 인터럽트)
현재 수행 중인 프로그램을 멈추고, 상태 레지스터와 PC 등을 스택에 잠시 저장한 뒤에 인터럽트 서비스 루틴으로 간다. (잠시 저장하는 이유는, 인터럽트 서비스 루틴이 끝난 뒤 다시 원래 작업으로 돌아와야 하기 때문)
만약 인터럽트 기능이 없었다면, 컨트롤러는 특정한 어떤 일을 할 시기를 알기 위해 계속 체크를 해야 한다. (이를 폴링(Polling)이라고 한다)
폴링을 하는 시간에는 원래 하던 일에 집중할 수가 없게 되어 많은 기능을 제대로 수행하지 못하는 단점이 있었다.
폴링 방식
인터럽트 요청 플래그를 차례로 비교하여 우선순위가 가장 높은 인터럽트 자원을 찾아 이에 맞는 인터럽트 서비스 루틴을 수행한다. (하드웨어에 비해 속도 느림) 사용자가 명령어를 사용해 입력 핀의 값을 계속 읽어 변화를 알아내는 방식
인터럽트 방식
MCU 자체가 하드웨어적으로 변화를 체크하여 변화 시에만 일정한 동작을 하는 방식
인터럽트는 발생 시기를 예측하기 힘든 경우에 컨트롤러가 가장 빠르게 대응할 수 있는 방법이다.
Spring
Test Code(JUint5)
테스트 코드를 작성해야 하는 이유
- 개발단계 초기에 문제를 발견할 수 있음
- 나중에 코드를 리팩터링 하거나 라이브러리 업그레이드 시 기존 기능이 잘 작동하는지 확인 가능함
- 기능에 대한 불확실성 감소
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest;
import org.springframework.test.context.junit4.SpringRunner;
import org.springframework.test.web.servlet.MockMvc;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
@RunWith(SpringRunner.class)
@WebMvcTest(controllers = HomeController.class)
public class HomeControllerTest {
@Autowired
private MockMvc mvc;
@Test
public void home_return() throws Exception {
//when
String home = "home";
//then
mvc.perform(get("/home"))
.andExpect(status().isOk())
.andExpect(content().string(home));
}
}
@RunWith(SpringRunner.class)
테스트를 진행할 때 JUnit에 내장된 실행자 외에 다른 실행자를 실행시킨다.
스프링 부트 테스트와 JUnit 사이의 연결자 역할을 한다고 생각하면 된다.
@WebMvcTest
컨트롤러만 사용할 때 선언이 가능하며, Spring MVC에 집중할 수 있는 어노테이션이다.
@Autowired
스프링이 관리하는 Bean을 주입시켜준다.
MockMvc
웹 API를 테스트할 때 사용하며, 이를 통해 HTTP GET, POST, DELETE 등에 대한 API 테스트가 가능하다.
mvc.perform(get("/home"))
/home 주소로 HTTP GET 요청을 한 상황이다.
. andExpect(status(). isOk())
결과를 검증하는 andExpect로, 여러 개를 붙여서 사용이 가능하다. status()는 HTTP Header를 검증하는 것으로 결과에 대한 HTTP Status 상태를 확인할 수 있다. 현재 isOK()는 200 코드가 맞는지 확인하고 있다.
JPA
JPA는 스프링이 제공하는 API가 아닌 자바가 제공하는 API이다.
자바 ORM 기술에 대한 표준 명세로, 자바 애플리케이션에서 관계형 데이터베이스를 사용하는 방식을 정의한 인터페이스다.
ORM(Object Relational Mapping)
ORM 프레임워크는 자바 객체와 관계형 DB를 매핑한다.
즉, 객체가 DB 테이블이 되도록 만들어주는 것이다.
ORM을 사용하면, SQL을 작성하지 않아도 직관적인 메서드로 데이터를 조작할 수 있다는 장점이 있다. ( 개발자에게 생산성을 향상해줄 수 있음 )
JPA는 애플리케이션과 JDBC 사이에서 동작하며, 개발자가 JPA를 활용했을 때 JDBC API를 통해 SQL을 호출하여 데이터베이스와 호출하는 전개가 이루어진다.
즉, 개발자는 JPA의 활용법만 익히면 DB 쿼리 구현 없이 데이터베이스를 관리할 수 있다.
JPA특징
- 객체 중심 개발 가능 SQL 중심 개발이 이루어진다면, CRUD 작업이 반복해서 이루어져야 한다.
- 하나의 테이블을 생성해야 할 때 이에 해당하는 CRUD를 전부 만들어야 하며, 추후에 칼럼이 생성되면 관련 SQL을 모두 수정해야 하는 번거로움이 있다. 또한 개발 과정에서 실수할 가능성도 높아진다.
- 생산성 증가 SQL 쿼리를 직접 생성하지 않고, 만들어진 객체에 JPA 메서드를 활용해 데이터베이스를 다루기 때문에 개발자에게 매우 편리성을 제공해준다.
- 유지보수 용이 쿼리 수정이 필요할 때, 이를 담아야 할 DTO 필드도 모두 변경해야 하는 작업이 필요하지만 JPA에서는 엔티티 클래스 정보만 변경하면 되므로 유지보수에 용이하다.
- 성능 증가 사람이 직접 SQL을 짜는 것과 비교해서 JPA는 동일한 쿼리에 대한 캐시 기능을 지원해주기 때문에 비교적 높은 성능 효율을 경험할 수 있다.
JPA 더티 체킹
트랜잭션 안에서 Entity의 변경이 일어났을 때, 변경한 내용을 자동으로 DB에 반영하는 것
@Transactional
public void cancelOrder(Long orderId) {
//주문 엔티티 조회
Order order = orderRepository.findOne(orderId);
//주문 취소
order.cancel();
}
orderId를 통해 주문을 취소하는 메서드다. 데이터베이스에 반영하기 위해선,
update와 같은 쿼리가 있어야 할 것 같은데 존재하지 않는다.
하지만, 실제로 이 메서드를 실행하면 데이터베이스에 update가 잘 이루어진다.
- 트랜잭션 시작
- orderId로 주문 Entity 조회
- 해당 Entity 주문 취소 상태로 Update
- 트랜잭션 커밋
이를 가능하게 하는 것이 바로 '더티 체킹(Dirty Checking)'이라고 보면 된다.
JPA에서는 트랜잭션이 끝나는 시점에 변화가 있던 모든 엔티티의 객체를 데이터베이스로 알아서 반영을 시켜준다.
즉, 트랜잭션의 마지막 시점에서 다른 점을 발견했을 때 데이터베이스로 update 쿼리를 날려주는 것이다.
- JPA에서 Entity를 조회
- 조회된 상태의 Entity에 대한 스냅숏 생성
- 트랜잭션 커밋 후 해당 스냅숏과 현재 Entity 상태의 다른 점을 체크
- 다른 점들을 update 쿼리로 데이터베이스에 전달
이때 더티 체킹을 검사하는 대상은 영속성 콘텍스트가 관리하는 Entity로만 대상으로 한다.
준영속, 비영속 Entity는 값을 변경할 지라도 데이터베이스에 반영시키지 않는다.
Spring Security - Authentication and Authorization
스프링 프레임워크에서는 인증 및 권한 부여로 리소스 사용을 컨트롤할 수 있는 Spring Security를 제공한다.
이 프레임워크를 사용하면, 보안 처리를 자체적으로 구현하지 않아도 쉽게 필요한 기능을 구현할 수 있다.
Spring Security는 스프링의 DispatcherServlet 앞단에 Filter 형태로 위치한다.
Dispatcher로 넘어가기 전에 이 Filter가 요청을 가로채서, 클라이언트의 리소스 접근 권한을 확인하고,
없는 경우에는 인증 요청 화면으로 자동 리다이렉트 한다.
Filter의 종류는 상당히 많다. 위에서 예시로 든 클라이언트가 리소스에 대한 접근 권한이 없을 때 처리를 담당하는 필터는 UsernamePasswordAuthenticationFilter다.
인증 권한이 없을 때 오류를 JSON으로 내려주기 위해 해당 필터가 실행되기 전 처리가 필요할 것이다.
API 인증 및 권한 부여를 위한 작업 순서는 아래와 같이 구성할 수 있다.
- 회원 가입, 로그인 API 구현
- 리소스 접근 가능한 ROLE_USER 권한을 가입 회원에게 부여
- Spring Security 설정에서 ROLE_USER 권한을 가지면 접근 가능하도록 세팅
- 권한이 있는 회원이 로그인 성공하면 리소스 접근 가능한 JWT 토큰 발급
- 해당 회원은 권한이 필요한 API 접근 시 JWT 보안 토큰을 사용
이처럼 접근 제한이 필요한 API에는 보안 토큰을 통해서 이 유저가 권한이 있는지 여부를 Spring Security를 통해 체크하고 리소스를 요청할 수 있도록 구성할 수 있다.
'CS' 카테고리의 다른 글
데이터베이스의 기본 개념 (0) | 2023.04.14 |
---|---|
HTTP 상태 코드 (2) | 2023.01.04 |
4주차CS스터디Network&Spring (0) | 2022.10.27 |
3주차CS스터디Network&Java (0) | 2022.10.14 |
2주차CS스터디Computer Architecture&Java (2) | 2022.09.28 |