1. Java에서 Solid 원칙 [디자인 패턴 활용]
1-1. SRP (단일 책임 원칙)
- 모든 클래스는 각각 하나의 책임만 가져야 하며, 수정할 이유는 단 한 가지여야 함
- 클래스는 그 책임을 완전히 캡슐화해야 함
- ex) 결제 클래스가 있으면 이 클래스는 오직 결제 기능만 책임지고, 만약 이 클래스를 수정해야 한다면 결제에 관련된 문제일 것.
1-2. OCP (개방-폐쇄 원칙)
- 소프트웨어 구성요소(컴포넌트, 클래스, 모듈, 함수)는 확정에는 열려 있어야 하지만 변경에는 폐쇄적이어야 함
- 기존의 코드를 변경하지 않으면서, 기능을 추가할 수 있도록 설계가 되는 원칙
- ex) 캐릭터 하나를 생성한다고 할 때 각 캐릭터마다 움직임이 다를 경우, 움직임 패턴 구현을 하위 클래스에 맡긴다면 캐릭터 클래스의 수정은 필요 없고(closed), 움직임 패턴만 재정의 하면 됨(open)
- 개방-폐쇄 원칙을 적용하기 위한 중요 메커니즘은 추상화와 다형성
1-3. LSP (리스코프 치환 원칙)
- 상위 타입은 항상 하위 타입으로 대체될 수 있어야 함
- 부모 클래스가 들어갈 자리에 자식 클래스를 넣어도 역할을 하는데 문제가 없어야 한다는 점


- 리스코프 치환 원칙은 다형성과 확장성을 극대화하며, 개방-폐쇄 원칙을 구성
1-4. ISP (인터페이스 분리 원칙)
- 각 역할에 맞게 인터페이스를 분리하는 것
- 최소한의 기능만 제공하면서 하나의 역할에 집중하라는 뜻
- SRP, ISP는 같은 문제에 대한 두 가지 다른 해결책이라고 볼 수 있음
- 가능한 최소한의 인터페이스를 사용하도록 하여 단일 책임을 강조
- 일반적으로 ISP보다 SRP 할 것을 권장
1-5. DIP (의존 관계 역전 원칙)
- 의존 관계를 맺을 때 고수준 모듈이 저수준 모듈에 직접 의존하는 것을 피해야 하는 것
- 고수준 모듈 : 변경이 없는 추상화 된 클래스(= 인터페이스)
- 저수준 모듈 : 위의 추상화 된 클래스나 인터페이스를 상속받은 구현체 클래스
코드 Note
public interface Pizza {
void prepare();
void bake();
void cut();
}
public class CheesePizza implments Pizza {
@Override
public void prepare() {
System.out.println("Preparing Cheese Pizza...");
}
@Override
public void bake() {
System.out.println("Baking Cheese Pizza...");
}
@Override
public void cut() {
System.out.println("Cutting Cheese Pizza...");
}
}
// 이렇게 Pizza라는 인터페이스(고수준 모듈)와 CheesePizza라는 구현 클래스(저수준 모듈)이 있을 때,
public class Chef {
private final Pizza pizza;
public Chef(Pizza pizza) {
this.pizza = pizza;
}
public void makePizza() {
pizza.prepare();
pizza.bake();
pizza.cut();
}
}
public class Main {
public static void main(String[] args) {
Pizza cheesePizza = new CheesePizza();
Chef chef = new Chef(cheesePizza);
chef.makePizza();
}
}
// Chef 클래스에서 의존성 주입을 할 때 변하기 쉬운 CheesePizza 클래스에 직접 의존하지 말고,
// 고수준 모듈인 Pizza 인터페이스를 의존하라는 의미.
2. Method Overriding & Overloading [디자인 패턴 활용]
- Overloading
- 상속과 관련x
- 한 클래스에서 같은 이름의 method를 여러개 정의하는 것(method 중복)
- 매개변수 타입이 다르거나 개수가 달라야 함
- 매개변수 순서가 달라야 함
- return type과 접근 제어자는 영향을 주지 않음
- Overriding
- 상속에서 나온 개념
- 상위 클래스(부모 클래스)의 method를 하위 클래스(자식 클래스)에서 상속받은 상태에서 본문 내용만 수정하는 것(method 재정의)
코드 Note
- 하는 일이 비슷하기 때문에 이름을 다르게 다 정의하면 코드가 복잡해지기에 이를 방지?
// Overload : method 중복
public class OverloadExam {
static void print() {
System.out.println("hello");
}
static void print(String a) {
System.out.println(a);
}
// static void print(String b) {
System.out.println(b);
// }
// static int print(String a) {
// return 100;
// }
public static void main(String[] args) {
print();
print(100);
print("java");
}
}
/*
interface A {
public void print();
}
*/
class A {
void print() {
System.out.println("a");
}
}
// class B implements A
class B extends A {
// override : method 재정의
public void print() {
Systemout.println("a");
Systemout.println("b");
}
}
public class OverrideExam {
public static void main(String[] args) {
B b = new B();
b.print();
// polymophism
A a = new B();
a.print();
A aa = new A();
aa.print();
}
}
3. 다형성 [디자인 패턴 활용]
- 부모 자료형으로 다양한 자식 자료형들을 처리할 수 있는 능력
- 부모 클래스의 인스턴스를 이용하여 자식 타입의 클래스를 다룸
- Overrriding을 통해 동일 이름의 method를 이용하여 다양한 자식 클래스의 method를 호출
4. CSR, SSR 차이 [API 설계 및 모범 사례]
4-1. CSR
- 사용자와의 상호작용이 많은 웹 애플리케이션에 적합 → js를 통해 클라이언트 측에서 페이지의 콘텐츠를 동적으로 생성하고 업데이트 하기 때문.
- 클라이언트 측에서 콘텐츠를 동적으로 생성하기에 사용자의 행동에 따라 실시간으로 페이지 콘텐츠를 업데이트 가능 → 풍부한 사용자 경험 제공

- User가 Website에 요청을 보냄
- CDN이 HTML 파일과 JS로 접근 가능한 링크를 클라이언트에게 전송 CDN : aws의 cloudflare. 엔드 유저의 요청에 물리적으로 가까운 서버에서 요청에 응답하는 방식
- 클라이언트는 HTML과 JS를 다운 → 이때 SSR과 달리 유저는 아무것도 볼 수 없음
- 생략
- 다운이 완료된 JS 실행. 데이터를 위한 API 호출 → 이때 유저들은 placeholder를 봄
- 서버가 API로부터의 요청에 응답
- API로부터 받아온 data를 placeholder 자리에 삽입. → 이제부터 페이지는 상호작용 가능

→ 서버에서 처리 없이 클라이언트로 보내주기 때문에 JS가 모두 다운 되고 실행이 끝나기 전까지 사용자는 볼 수 있는게 없음
4-2. SSR
- 초기 페이지 로딩 속도가 빠르고 검색 엔진 최적화(SEO)에 유리 → 서버에서 렌더링된 페이지가 클라이언트에게 전송되기 때문에 검색 엔진이 콘텐츠를 쉽게 Indexing 가능
- js가 비활성화된 환경에서도 페이지를 볼 수 있게 해줌

- User가 Website 요청을 보냄
- Server는 ‘Ready to Render’ → 즉시 렌더링 가능한 HTML 파일을 만듦 (리소스 체크, 컴파일 후 완성된 HTML 콘텐츠로 만듦)
- 클라이언트에 전달되는 순간, 이미 렌더링 준비가 되어있기 때문에 HTML은 즉시 렌더링 됨 → 하지만 사이트 자체 조작은 불가능(JS가 읽히기 전)
- 클라이언트가 JS를 다운받음
- 다운 받는 사이 유저는 콘텐츠를 볼 수 있지만 사이트 조작은 못함 → 이때 사용자 조작을 기억하고 있음
- 브라우저가 JS Framework를 실행
- JS까지 성공적으로 컴파일 되었기 때문에 기억하고 있던 사용자 조작이 실행되고, 이제 웹 페이지는 상호작용이 가능
차이점
- 웹 페이지 로딩 시간
- 첫 페이지 로딩 시간
- CSR의 경우 HTML, CSS와 모든 스크립트들을 한 번에 로드. 반면 SSR은 필요한 부분의 HTML과 스크립트만 로드 → SSR이 더 빠름
- 나머지 로딩 시간
- 첫 페이지 로딩 후, 사이트의 다른 곳으로 이동하는 식의 동작을 가정 → CSR은 이미 첫 페이지 로딩 시 나머지 부분을 구성하는 코드를 받아와서 빠름. 반면 SSR은 첫 페이지를 로딩한 과정을 정확하게 다시 실행함(더 느림)
- 서버 자원 사용
- 매번 서버에 요청을 하기 때문에 SSR이 서버 자원을 더 많이 사용
- 반면 CSR은 클라이언트에 일을 몰아주기에 서버 부하가 적음
선택 기준
- 두개 중 어떤 방식을 선택할지는 프로젝트의 요구 사항과 목표에 따라 달라짐. SEO가 중요하거나 초기 페이지 로딩 속도가 중요한 프로젝트의 경우 SSR을, 사용자 경험과 상호작용이 중요한 웹 애플리케이션의 경우 CSR 선택이 좋음
5. AJAX 통신 [API 설계 및 모범 사례]
- JS를 통해 비동기식으로 서버에 데이터를 요청하여 필요한 데이터를 받아와 전체 페이지를 새로 고치지 않고 변경이 필요한 페이지 부분만을 고치는 기술
- 주목적은 화면 전환 없이 클라이언트와 서버간의 정보를 교환하기 위함

→ 동기식은 요청 후 응답을 받아야 다음 동작이 이루어지고 비동기식에서는 요청을 보낸 후 응답과는 상관없이 동작하는 방식. → 이러한 이유로 HTTP 전송 중에도 클라이언트가 웹 애플리케이션과 상호작용을 할 수 있는 것.
AJAX 사용법
$.ajax({
url : requestUrl,
type : 'DELETE', // 요청 방식
async : true, // 디폴트값 true (false = 동기)
data : JSON.stringify(requestParam), // 전달 파라미터
dataType : "json", // 서버에서 반환되는 데이터 형식 (text, html, xml, json 등)
timeout : 10000,
contentType : "application/json", // 서버에서 데이터를 보낼 때 사용 (content-type 헤더 값)
befireSend:function(){
},
complete:function(){
},
success:function(){
},
error:function(){
},
fail:function(){
}
});
}
6. JSON [API 설계 및 모범 사례]
- Java Script 객체 표기법으로 데이터를 쉽게 교환하고 저장하기 위한 텍스트 기반의 데이터 교환 표준
- 텍스트 기반이기에 다양한 프로그래밍 언어에서 데이터를 읽고 사용 가능
기본형태
// Key와 Value의 쌍으로 이루어진 구조.
{ key : value}
// 여러 데이터를 나열할 경우 쉼표 사용
{key1 : value, key2 : value2}
// 객체(Object)는 중괄호로 묶어서 표현, 배열은 대괄호로 묶어서 표현
{key1 : {inKey : inValue}, key2 : [arr1, ar2, arr3] }
{"판매자정보" : {"이름" : "신민재", "지역" : "부산" }, "판매품목" : ['옷', '바지', '신발'] }
// ----------------------------------------------
// 데이터 값으로 다양한 타입 사용 가능
// 숫자 문자열 불리언 객체 배열 널
[ 1, "str", true, {inKey : "value"}, ["일", "이"], null]
JSON 사용
- JSON.stringify(arg) : 객체를 문자열로 변환
var json = {"test" : "value"}
var incodingData = JSON.stringify(json);
// console.log(incodingData);
- JSON.parse(arg) : 문자열을 객체로 변환
var str = '{"test" : "value"}';
var parsingData = JSON.parse(str);
// console.log(incodingData);
예제 - [이름, 성별, 나이] 객체 생성
var Characters = [
{
'이름' : '신민재',
'성별' : '남',
'나이' : 28
},
{
'이름' : '신민지',
'성별' : '여',
'나이' : 27
}
]
// 여기서 성인여부 항목 추가시
Character.성인여부 = true;
// or
Character["성인여부"] = true;
key를 동적으로 생성해야 하는 경우
var key = "직접잡은범인";
Character.key = 0; // X
// {이름 : "신민재", 성별: "남", 나이: "28", key:0}
// 이렇게 하면 key값이 올바르게 추가됨. 즉 동적으로 생성해야한다면 이와 같은 방법 사용
var key = "직접잡은범인";
Character[key] = 0; // X
// {이름 : "신민재", 성별: "남", 나이: "28", 직접잡은범인:0}
7. HTTP 401, 403 [서비스 인증과 권한 부여]
401 Unauthorized
// 인증 실패 (클라이언트가 인증 없이 요청했거나, 인증 도중 실패
@ExceptionHandler(Exception401.class)
public String ex401(Exception e) {
return Script.href("인증되지 않았습니다", "/login-form");
}
- 인증(Authentication) : 본인이 누구인지 확인 → 로그인
- 인가(Authorization) : 권한 부여
- 오류 메시지가 Unauthorized이지만 인증되지 않음
403 Forbidden
// 권한 실패 (인증은 되어있으나, 삭제하려는 게시글이 내가 적은 글이 아님)
@ExceptionHandler(Exception403.class)
public String ex403(Exception e) {
return Script.back(e.getMessage());
}
- 서버가 요청을 이해했지만 승인을 거부함
- 주로 인증 자격 증명은 있지만 접근 권한이 불충분한 경우
- Admin 등급이 아닌 사용자가 로그인 했지만, Admin 등급의 리소스에 접근하는 경우 발생
8. JWT와 Session 차이 [스프링 시큐리티와 JWT 적용]
Session 방식

- 클라이언트가 로그인을 위해 인증 정보를 서버에 전송
- 서버는 메모리에 사용자를 저장하고, 세션 아이디를 쿠키로 전달
- 클라이언트는 쿠키에 저장된 세션 아이디를 이용하여 요청
- 서버는 일치하는 세션 아이디를 메모리에서 검색한 후 응답
- 서버 인증 기반의 문제점?
- 세션을 DB에 저장해서 탐색하기 때문에 유저가 늘어날수록 서버의 램 과부하
- 여러 개의 프로세스를 돌리거나 서버 컴퓨터를 추가하는 것이 쉽지 않음
- 쿠키는 단일 도메인 및 서브 도메인에서만 작동하도록 설계되어 있어서 여러 도메인에서 관리하기 힘듦
JWT (Json Web Token)
- JWT는 JSON 개체로 안전하게 전송하기 위한 국제 표준 (RFC 7519)
- 디지털 서명이 되어 있어서 확인하고 신뢰 가능

- 클라이언트가 로그인을 위해 인증 정보를 서버에 전송
- 서버는 secret 정보를 이용하여 JWT를 생성하고 클라이언트에게 전달
- 클라이언트는 로컬 혹은 브라우저에게 저장해뒀던 JWT를 이용하여 요청
- 서버는 JWT가 일치하는지 확인한 후, 응답
JWT 구조 (xxxxx.yyyyy.zzzzz)
- Header
- 토큰 타입과 어떤 알고리즘이 쓰였는지 나타냄
{
"alg" : "HS256",
"typ" : "JWT"
}
// 이 json은 Base64Url로 인코딩 된 JWT의 첫번째 부분
- Payload
- 토큰의 두번째 부분은 사용자 및 정보가 담겨있는 payload 부분
- payload에 담긴 정보는 인코딩만 되어 있어서 누구라도 디코딩하여 확인할 수 있으므로 중요한 정보를 담으면 보안상 위험
- Signature
- 마지막 Signature 영역을 생성하려면 인코딩 된 header, payload, secret, 헤더에 지정된 알고리즘을 이용하여 해싱
- 만약 어느 하나라도 일치하지 않으면 완전히 다른 값을 갖게 됨
토큰 작동 방식
- 로그인하면 JSON 웹 토큰이 반환
- 토큰은 자격 증명이므로 보안 문제를 방지하기 위해 세심한 주의 필요
- 일반적으로 토큰을 필요 이상으로 오래 보관해서는 안되며 보안이 취약하기에 민감한 세션 데이터를 부라우저 저장소에 저장해서는 안됨
- 앱에서는 설치된 앱 별로 안전한 저장 공간이 제공되지만 웹에서는 세션 인증이 나은 선택일 수 있음
- Token 인증방식의 장점?
- 무상태와 확장성, 토큰은 클라이언트쪽에만 저장되므로 stateless 하며, 서버를 확장하기에 적합한 환경을 제공
- 쿠키를 사용함으로 인해 발생하는 보안 취약점이 사라짐
- 토큰을 사용하여 다른 서비스에도 권한 공유
Session과 JWT 차이
- 서버에 인증 정보를 저장하지 않는다는 점이 상이
- 클라이언트의 요청마다 인증을 위해 DB를 탐색하는 과정이 필요 없고 저장 공간도 필요 없음
CSRF 이슈
- Cross Site Request Forgery
POST /email/change HTTP/1.1
Host: vulnerable-website.com
Content-Type: application/x-www-form-urlencoded
Content-Length: 30
Cookie: session=yvthwsztyeQkAPzeQ5gHgTvlyxHfsAfE
email=wiener@normal-user.com
<html>
<body>
<form action="https://vulnerable-website.com/email/change" method="POST">
<input type="hidden" name="email" value="pwned@evil-user.net" />
</form>
<script>
document.forms[0].submit();
</script>
</body>
</html>
- 보완책
- CSRF 토큰 - CSRF 토큰은 서버 측 응용 프로그램에서 생성하고 클라이언트와 공유하는 고유한 비밀 및 예측 불가능한 값
- 양식 제출과 같은 중요한 작업을 수행할 때 클라이언트는 요청에 올바른 CSRF 토큰을 포함하여 전달
- SameSite 쿠키 - 웹 사이트의 쿠키가 다른 웹 사이트에서 발생하는 요청에 포함되는 시기를 결정하는 브라우저 보안 메커니즘
- 중요한 작업을 수행하기 위한 요청에는 일반적으로 인증된 세션 쿠키가 필요하므로, 적절한 SameSite 제한으로 인해 공격자가 사이트 간 이러한 작업을 트리거 X
- Referer 기반 검증 - 일부 응용 프로그램은 HTTP Referer 헤더를 사용하여 CSRF 공격을 방어
- 일반적으로 요청이 응용 프로그램의 자체 도메인에서 발생했는지 확인
9. Cross Flatform [Flutter와 Meterial 디자인 활용]
- 교차를 뜻하는 Cross와 Flatform의 합성어로, 다양한 플랫폼에서 사용할 수 있는 뜻
- 하나의 개발 언어로 안드로이드와 iOS에서 사용 가능한 앱 개발
- Flutter (구글)
- React Native (페이스북)
- Xamarin (마이크로소프트)
장점
- 하나의 개발 언어와 도구로 안드로이드와 iOS 모두에서 동작할 수 있는 앱을 만들 수 있다는 것
- Native 앱과 비교했을 때 개발과 운영에 필요한 비용과 시간을 절약 가능
- Flutter는 안드로이드, iOS뿐 아닌 웹, 리눅스, 윈도우 등에서 동작하는 데스크탑 앱 개발까지 지원하는 툴
단점
- Native 앱만큼 높은 성능과 OS에서 제공하는 API를 활용하기 어려움
- OS에서 새롭게 API를 업데이트 했다면, 크로스 플랫폼에서 지원해 줄 때까지 즉시 사용 불가
- 크로스 플랫폼 측 버그 발생 시 해당 플랫폼 개발자가 버그를 해결할 때까지 기다릴 수 밖에 없음
- Airbnb는 초기에 React Native로 서비스를 개발/운영 하다가 위와 같은 이슈로 2018년부터 Native 앱 활용
10. 동기 & 비동기 [Flutter Animation 처리]
동기
- 작업이 순차적으로 진행 → 한 작업이 시작되면 해당 작업이 완료될 때까지 다른 작업이 기다려야함
- 동기 방식은 호출한 함수 또는 작업이 반환될 때까지 대기하는 동안 실행 흐름이 차단되는 특징
- 일반적으로 간단하고 직관적인 코드 작성이 쉬우나 여러 작업이 동시에 실행되어야 하는 경우, 각 작업의 완료를 기다리는 동안 시간이 소요되어 전체 프로세스의 성능이 저하될 수 있음
- 또한 한 작업이 지연되면 다른 작업들도 모두 지연되는 문제 발생 가능
비동기
- 작업이 독립적으로 실행 → 작업의 완료 여부를 기다리지 않고 다른 작업을 실행할 수 있는 방식
- 작업 시작 시 해당 작업이 완료될 때까지 기다리지 않고 다음 코드 실행 가능
- 주로 I/O 작업이나 네트워크 요청과 같은 시간이 오래 걸리는 작업에 유용
- 이러한 작업을 비동기적으로 처리하면 프로그램은 작업이 완료되기를 기다리는 동안 다른 작업을 처리할 수 있으므로 전체적인 성능이 향상
- 비동기 방식은 callback, promise, async/await 등의 메커니즘을 통해 구현 가능
11. 데이터의 불변성이 중요한 이유 [Flutter 상태관리]
- 불변성 : 값이나 상태를 변경할 수 없는 것
- 프로그래밍에서의 불변성은 데이터 원본의 훼손을 막는 것 → 어떤 값을 직접적으로 변경하지 않고 새로운 값을 만들어내는 것
- JS에서는 원시타입은 불변성을 지니지만, 객체타입은 값이 변할 수 있음
원시타입
Boolean, Number, String, null, undefined, Symbol
let str = "example1";
str = "example2";
console.log(str) // example2
// 값이 변경된 것이 아닌, 새로 선언된 값이 메모리에 저장되고 변수가 새로운 메모리를 가리키는 것.
let name = 'SHIN';
let new_name = name; // -> new_name이 name이 가리키고 있는 메모리를 같이 가리킴
name = 'KIM';
console.log(new_name); // SHIN
console.log(name); // KIM
객체 타입
var person = {
name: 'SHIN',
age: 28,
}
var new_person = person;
person.name = 'KIM';
console.log(person.name); // 'KIM'
console.log(new_person.name); // 'KIM'
// 객체 타입은 원시 타입과 다르게 이름이 모두 변경됨
// 객체는 객체 내부의 값을 변경하면 객체를 참조하고 있는 다른 값들도 모두 변경
불변성을 지켜야 하는 이유
- 리액트는 상태값을 업데이트 할 때 얕은 비교를 수행함
- 리액트는 객체의 속성 하나하나를 비교하는게 아닌, 참조값만 비교하여 상태 변화를 감지
- 얕은 비교 : 객체, 배열, 함수와 같은 참조 타입들을 실제 내부 값까지 비교하지 않고 동일 참조인지(동일한 메모리 값을 사용하는지)를 비교하는 것
Ex)
- 컴포넌트를 ReRendering 해야하는 상황이 있다고 가정하고 타입이 배열인 state를 바꿈
- 이때 state.push(1)을 통해 state 배열에 직접 접근하여 요소를 추가
- 우리는 push 전과 다른 값이라고 생각하지만, 리액트는 state라는 값은 새로운 참조값이 아니기 때문에 이전과 같은 값이라고 인식하고 ReRendering 하지 않음
불변성의 장점
- 효율적인 상태 업데이트 (얕은 비교 수행)
- 계산 리소스를 줄여주기 때문에 리액트는 효율적으로 상태 업데이트 가능
- 사이드 이펙트 방지 및 프로그래밍 구조의 단순성
- 원시타입은 불변성 특징을 가지고 있지만 참조타입인 객체나 배열의 경우 새로운 값을 변경할 때 원본 데이터가 변경됨 (불변성이 지켜지지 않음). 이렇게 원본 데이터가 변경될 경우, 이 원본 데이터를 참조하고 있는 다른 객체에서 예상치 못한 오류 발생 가능 → 프로그래밍의 복잡도 상승
- 즉 불변성을 지키면 이를 방지할 수 있음
불변성을 지키는 법
// Spread 문법
// 원시 타입
const [number, setNumber] = useState(0)
setState(10)
// 참조 타입
const [person, setPerson] = useState({ name: '', age: 28 })
setState({...person, name: 'SHIN'})
setUsers(
users.map(user =>
user.id === id ? { ...user, name: "SHIN" } : user
)
);
// immer 사용
import produce from "immer";
const user = [
{
name: "SHIN",
age: 28
},
{
name: "KIM",
age: 32
}
];
const new_user = produce(user, draft => {
draft.push({ name: "LEE" });
draft[1].age = 29;
});
12. Hash 알고리즘 [Flutter 상태관리]
- 임의의 길이를 가진 데이터를 고정된 길이의 hash 값으로 변환하는 알고리즘
- 이 알고리즘은 입력 데이터에 대해 일관된 hash 값을 생성하고, 입력 데이터가 조금만 변경되어도 완전 다른 hash 값을 반환하는 특징을 가짐
- 일반적으로 hash 함수라고도 불리며, 다양한 분야에서 데이터 무결성 검사, 데이터 식별, 암호화, 암호화 인증, 자료구조 등에 활용
- 대표적인 종류
- MD5 (Message Digest Algorithm5)
- SHA (Secure Hash Algorithm)
- 충돌(collision)을 최소화하여 hash 값 중복 방지 및 안전성과 보안성을 강화하기 위해 설계
목적
- 데이터 무결성 검사 : 데이터가 변경되지 않았는지 확인을 위해 hash 값 사용
- 데이터 식별 : 데이터를 고유하게 식별하기 위해 hash 값 사용
- 암호화 : 암호화 알고리즘에서 키를 생성하거나 데이터를 변환하기 위해 사용
- 암호 인증 : 사용자의 비밀번호나 인증 토큰 등을 안전하게 저장하고 검증을 위해 사용
- 자료구조 : hash 테이블, hash 집합, hash map 등 자료구조를 구현하기 위해 사용
hash 알고리즘이 빠른 이유
- 고정된 출력 크기
- 입력 데이터의 크기에 관계없이 항상 고정된 크기의 hash 값(hash 코드 또는 hash 체크섬)을 생성 → 입력 데이터의 크기에 관계없이 일정한 연산 시간을 유지하도록 함
- 단방향 함수(일방향 함수)
- hash 값에서 기존 데이터로의 복원이 어렵거나 불가능
- 이로 인해 hash 알고리즘은 데이터의 비밀성을 유지하는 데에 적합, 암호화 및 인증 등의 보안 기능에 활용 가능
- 고속 연산
- hash 함수의 수행 시간이 입력 데이터의 크기와는 관련이 없고, 일정한 시간 내에 연산이 완료되기 때문
- hash 알고리즘은 대부분의 경우 빠른 실행 속도를 제공하여 대량의 데이터를 효율적으로 처리 가능
- 충돌 저항성
- 좋은 hash 알고리즘은 입력 데이터의 작은 변경에도 전혀 다른 해시 값을 생성하도록 설계(충돌 저항성)
- 충돌은 서로 다른 입력 데이터가 동일한 hash 값을 가질 때 발생하는 현상
- 충돌을 최소화하고 안정적인 분포를 갖는 hash 알고리즘은 검색 성능을 향상 시키고 데이터의 고르고 균형 잡힌 분산을 도모
- 하드웨어 최적화
- 일부 hash 알고리즘은 하드웨어에서 효율적으로 구현될 수 있도록 최적화 되야함
- 이러한 최적화는 CPU 명령어 세트 확장, 병렬 처리, 하드웨어 가속 등을 활용하여 hash 알고리즘의 실행 속도를 높이는데 도움
결론
- 고정된 출력 크기, 단방향 함수, 고속 연산, 충돌 저항성 및 하드웨어 최적화 등의 특징으로 인해 빠른 속도를 제공
13. Http Header에서 Content-Type & User-Agent [Flutter JWT 토큰 관리] - 출처 MDN
Content-Type
- 클라이언트와 서버 간 주고받는 데이터의 유형을 지정
- Ex) 서버가 클라이언트에게 HTML 파일을 보낼 때 Content-Type을 text/html 로 설정하여 HTML로 해석하도록 함
- 클라이언트가 데이터를 서버로 보낼 때도 Content-Type을 사용해 서버가 어떤 형식의 데이터를 처리해야 하는지 알려줌
User-Agent
- 클라이언트가 서버에 자신을 식별하기 위해 사용하는 문자열
- 요청을 보내는 소프트웨어의 종류(ex:웹 브라우저, 웹 크롤러), 버전, 운영체제 등을 포함하여 서버가 클라이언트의 특성을 이해하고 이에 맞는 응답을 제공할 수 있도록 도움
- Ex) 서버는 User-Agent 정보를 사용해 특정 브라우저에서만 동작하는 기능을 활성화하거나 모바일 기기용 페이지를 제공
14. REST API [Retrofit 구현과 활용]
- 하나의 URI가 하나의 고유한 리소스를 대표하도록 설계된 개념 http://localhost/reply/list?bno=1 → http://localhost/reply/list/1 http://localhost/reply/list?bno=7 → http://localhost/reply/list/7
15. MVC 패턴 [Restrofit 구현과 활용]
- 코드 재사용이 유용하며 사용자 인터페이스와 응용프로그램 개발에 소요되는 시간을 현저하게 줄여줌
MVC 구성요소
- Model : 소프트웨어 응용과 그와 관련된 고급 클래스 내의 논리적 데이터 기반 구조를 표현
- View : 사용자 인터페이스 내의 구성요소들을 표현(사용자에게 보여지는 화면)
- Controller : Model과 View를 연결하고 흐름을 제어하는 클래스. Model과 View 사이의 정보를 교환하는데 사용
16. Interceptor [Retrofit 구현과 활용]
- Spring MVC에서 인터셉터는 웹 어플리케이션 내에서 특정한 URI 호출을 ‘가로채는’ 역할
- 이를 활용하면 기존 컨트롤러의 로직을 수정하지 않고도 사전이나 사후에 제어 가능 → 요청과 응답을 가로채서 원하는 동작을 추가하는 역할
- Ex) 세션을 통한 인증을 쉽게 구현 가능
- 요청을 받아 들이기 전, 세션에서 로그인 한 사용자가 있는지 확인해보고 없다면 로그인 페이지로 redirect 시킴
- 인터셉터가 없다면 모든 컨트롤러마다 해당 로직을 넣어야 해서 비효율적
- 주기적으로 결과 페이지에 등장하는 데이터들을 인터셉터에서 응답을 가로채어 데이터를 추가한 후 보낼 수 도 있음 → 메일 알림 개수 조회 후 추가, 헤더 데이터 같은 것들
Filter와 혼동 가능!
Filter
- DS 처리 전 후에 동작하여 사용자의 요청이나 응답의 최전방에 존재
- 스프링의 독자적 기능이 아닌 Java Servlet에서 제공

public interface Filter {
public default void init(FilterConfig filterConfig) throws ServletException {}
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException;
public default void destroy() {}
}
Filter Interface의 3가지 Method
- init : 필터가 웹 컨테이너에 생성될 때 실행
- doFilter : Request, Response가 필터를 거칠 때 수행되는 메소드, 체인을 따라 다음에 존재하는 필터로 이동
- destroy : 필터가 소멸될 때 실행됨
Interceptor와 Filter 공통점
- Servlet 기술의 Filter와 Spring MVC의 HandlerInterceptor는 특정 URI에 접근할 때 제어하는 용도로 사용된다는 공통점을 가지고 있음
차이점 : 영역의 차이
- 실행 시점에 속하는 영역(Context)

Share article