diff --git a/docs/posts/architecture/raft.mdx b/docs/posts/architecture/raft.mdx new file mode 100644 index 0000000..a50986d --- /dev/null +++ b/docs/posts/architecture/raft.mdx @@ -0,0 +1,59 @@ +--- +title: RAFT 합의 알고리즘 +type: 'concept' +language: 'Architecture' +tags: + - RAFT + - Availability +dateModified: 2025.10.19 +--- + +import { ImageContainer } from '/src/components/ui/ImageContainer.tsx'; + +# RAFT + +## 클러스터란? + +RAFT에 설명하기에 앞서 클러스터 용어의 설명이 선행되어야 합니다. 클러스터(Cluster)는 여러 대의 서버(노드)가 하나의 시스템처럼 동작하도록 구성된 집합체입니다. + +이를 통해 시스템은 장애 발생 시에도 서비스를 지속할 수 있는 **고가용성(HA)** 과 **데이터 일관성**을 보장할 수 있습니다. + +## RAFT 란? + +**RAFT(Raft Consensus Algorithm)** 는 분산 시스템에서 **여러 노드 간 데이터 일관성과 합의를 유지하기 위한 리더 선출 기반의 합의 알고리즘**으로 리더 선출과 로그 복제를 통해 일관성을 확보합니다. + +클러스터 내 노드는 리더(Leader)와 팔로워(Follower)로 구분되며, 리더는 주기적으로 하트비트(Heartbeat) 신호를 전송하여 팔로워의 상태를 모니터링하고 클러스터의 정상 동작을 유지합니다. + + + +## 리더 선거 + +리더는 주기적으로 하트비트를 전송하여 팔로워의 상태를 점검합니다. + +팔로워 노드는 리더로부터 일정 시간(ex: 150~300ms) 하트비트 메시지를 받지 못하면 **리더 장애로 판단**하고, 스스로 **후보자**(Candidate)로 전환한 뒤 새로운 리더를 선출하는 **텀**이 시행 됩니다. + +텀은 리더 선출하는 임의의 기간으로, 각 후보 노드 중 과반수 표를 받으면 리더 선출이 완료됩니다. + +리더 선출이 성공적으로 완료되면 새 리더가 조정하는 정상적인 운영을 진행합니다. + + + +## 로그 복제 + +리더는 클라이언트의 요청을 로그 형태로 기록하고, 이를 모든 팔로워에게 복제합니다. 과반수의 팔로워가 로그를 수신·저장했다는 응답을 보내면 리더는 해당 로그를 **커밋(Commit)** 하며, 커밋된 로그는 모든 노드에 동일하게 적용되어 클러스터의 **데이터 일관성**을 유지합니다. + +## RAFT를 왜 사용하는가? + +RAFT를 사용하면 다중 환경 시스템에서 클러스터 내의 고가용성을 보장하기 편리하기 때문입니다. + +예를 들어 MongoDB의 Replica Set 구조에서는 데이터를 저장할 때 리더에 최초 저장, 이후 팔로워가 리더의 데이터를 참고해서 복제하는 방식으로 동작합니다. 이 때 팔로워는 복제(Replication)되었기 때문에 백업 서버로 활용될 수 있으며, 고가용성(High Availability)을 위한 교체 서버로 활용할 수 있기 때문입니다. + +## RAFT 합의 알고리즘 실 사용 사례 + +다양한 곳에서 RAFT 합의 알고리즘을 활용함 + +- IBM MQ +- MongoDB +- RabbitMQ + +- Apache Kafka (KRaft) diff --git a/docs/posts/be-interview/di-ioc.mdx.mdx b/docs/posts/be-interview/di-ioc.mdx.mdx new file mode 100644 index 0000000..aed1cd2 --- /dev/null +++ b/docs/posts/be-interview/di-ioc.mdx.mdx @@ -0,0 +1,47 @@ +--- +title: DI, IoC란 무엇인가요? +type: 'interview' +language: 'BE-Interview' +tags: + - DI + - IoC +dateModified: 2025.09.19 +--- + +## 답변 + +### DI (Dependency Injection) + +**의존성 주입**이란 어떤 클래스가 필요로 하는 의존성을 외부에서 주입받는 것을 말합니다. +전통적인 방식에서는 클래스 내부에서 필요한 객체를 직접 생성했지만, DI를 사용하면 외부에서 생성된 객체를 받아 사용합니다. + +Spring에서는 **IoC 컨테이너**가 객체의 생성과 의존성 주입을 담당합니다. +DI 방법에는 필드 주입, Setter 주입, 생성자 주입이 있으며, 불변성과 테스트 용이성 때문에 **생성자 주입이 권장**됩니다. + +### IoC (Inversion of Control) + +**제어의 역전**이란 프로그램의 제어 권한을 개발자가 아닌 프레임워크에 위임하는 것을 말합니다. +일반적인 프로그램에서는 개발자가 직접 객체를 생성하고 메서드를 호출하지만, IoC에서는 프레임워크가 이러한 흐름을 제어합니다. + +Spring에서 **ApplicationContext**는 BeanFactory를 확장한 IoC 컨테이너 구현체로, Bean의 생성·의존성 주입·생명주기를 관리합니다. +컴포넌트 스캔을 통해 `@Component`, `@Service`, `@Repository` 등의 어노테이션이 붙은 클래스를 자동으로 Bean으로 등록합니다. + +### DI와 IoC의 관계 + +IoC는 제어권을 역전시키는 **설계 원칙**이고, DI는 이를 구현하는 **구체적인 기법**입니다. +Spring에서는 IoC 컨테이너가 Bean을 생성하고 의존성을 주입하는 방식(DI)으로 IoC 원칙을 구현합니다. + +즉, IoC는 "무엇을"에 해당하는 개념이고, DI는 "어떻게"에 해당하는 구현 방법입니다. + +### 왜 중요한가? + +1. **결합도 감소**: 클래스 간의 의존성을 줄여 코드의 유연성을 높입니다. 구체 클래스가 아닌 인터페이스에 의존하게 되어 구현체 교체가 용이합니다. +2. **테스트 용이성**: Mock 객체를 쉽게 주입하여 단위 테스트가 가능합니다. 실제 DB나 외부 API 없이도 테스트할 수 있습니다. +3. **재사용성 향상**: 의존성을 외부에서 주입받아 다양한 환경에서 재사용 가능합니다. 같은 클래스를 다른 의존성과 함께 사용할 수 있습니다. +4. **유지보수성**: 변경사항이 다른 클래스에 미치는 영향을 최소화합니다. 한 곳의 수정이 전체 시스템에 파급되지 않습니다. + +### 참고자료 + +[Spring Docs: IoC container](https://docs.spring.io/spring-framework/reference/core/beans.html) +[Spring Docs: Dependency Injection](https://docs.spring.io/spring-framework/reference/core/beans/dependencies/factory-collaborators.html) +[Martin Fowler: Inversion of Control](https://martinfowler.com/bliki/InversionOfControl.html) diff --git a/docs/posts/be-interview/java-exception.mdx b/docs/posts/be-interview/java-exception.mdx new file mode 100644 index 0000000..8a4d240 --- /dev/null +++ b/docs/posts/be-interview/java-exception.mdx @@ -0,0 +1,30 @@ +--- +title: Java에서 Checked Exception과 Unchecked Exception에 대해서 설명해 주세요. +type: 'interview' +language: 'be-interview' +tags: + - Java +dateModified: 2025.10.05 +--- + +## 답변 + +Checked Exception은 컴파일할 때 예외 처리를 강제하는 예외입니다. 자바에서는 IOException, SQLException 등이 해당됩니다.
+Checked Exception이 발생할 수 있는 메서드를 호출하는 경우, throws를 사용하여 호출자에게 예외를 위임하거나 try-catch 문을 통해 반드시 예외를 처리해야 합니다. + +Unchecked Exception은 컴파일할 때 예외 처리를 강제하지 않는 예외입니다. 자바에서는 RuntimeException을 상속하는 예외들이 해당됩니다.
+주로 프로그래머의 실수나 코드 오류로 인해 발생하며, 별도의 예외 처리를 하지 않아도 컴파일이 가능합니다. + +### Q. 언제 사용해야 할까요? + +Checked Exception은 파일 입출력 오류, 데이터베이스 연결 실패 등 클라이언트가 예측하고 복구할 수 있는 예외에 적합합니다.
+Uncheked Exception은 코드 오류, 논리적 결함 등 프로그래머의 실수로 발생하며 클라이언트가 복구할 수 없는 예외에 적합합니다. + +### 정리 + +| 구분 | Checked Exception | Unchecked Exception | +| :---------------- | :------------------------------- | :----------------------------------------------------- | +| **확인 시점** | 컴파일 시점 | 런타임 시점 | +| **처리 여부** | 반드시 예외를 처리해야 함 | 명시적으로 예외를 처리하지 않아도 됨 | +| **트랜잭션 처리** | 예외 발생 시 Rollback 되지 않음 | 예외 발생 시 Rollback 됨 | +| **종류** | `IOException`, `SQLException` 등 | `NullPointerException`, `IndexOutOfBoundsException` 등 | diff --git a/docs/posts/database/lock.mdx b/docs/posts/database/lock.mdx new file mode 100644 index 0000000..d40cef5 --- /dev/null +++ b/docs/posts/database/lock.mdx @@ -0,0 +1,75 @@ +--- +title: 'MySQL 엔진의 락(Lock) 종류' +type: 'concept' +language: 'Database' +tags: + - Database + - Lock + - SQL +dateModified: 2025.09.28 +--- + +# MySQL 엔진 레벨의 락 종류 + +락(Lock, 잠금)은 데이터베이스 공유 자원에 여러 커넥션이 동시 접근하는 경우 데이터의 일관성과 무결성을 유지하기 위해 사용되는 메커니즘입니다.
+MySQL에서의 락(Lock, 잠금)은 크게 MySQL 엔진 레벨의 락과 스토리지 엔진 레벨의 락으로 나눌 수 있습니다.
+**MySQL 엔진 레벨의 락**의 종류는 **글로벌 락,테이블 락, 네임드 락, 메타데이터 락**이 있습니다.
+ +## 1. 글로벌 락(Global Lock) + +글로벌 락은 MySQL에서 제공하는 락 중에 가장 범위가 가장 크며, MySQL 서버 전체에 영향을 미칩니다.
+한 세션에서 글로벌 락을 획득하면 다른 세션에서 SELECT를 제외한 DDL, DML문을 실행하는 경우 대기 상태가 됩니다.
+ +`FLUSH TABLES WITH READ LOCK` 명령으로 획득할 수 있습니다.
+ +MyISAM이나 MEMORY 테이블에 `mysqldump`로 백업을 받는 경우 글로벌 락을 사용해야 합니다.
+반면, InnoDB 스토리지 엔진은 트랜잭션을 지원하기 때문에 `mysqldump`로 백업을 하는 경우,
+`--single-transaction` 옵션을 사용하면 온라인 상태에서도 백업이 가능하고 글로벌 락을 사용할 필요가 없습니다. + +## 2. 테이블 락(Table Lock) + +테이블 락은 개별 테이블 단위로 설정되는 잠금이며, 명시적 또는 묵시적으로 락을 획득할 수 있습니다.
+ +명시적으로는 `LOCK TABLES tabled_name [ READ | WRITE ]` 명령으로 락을 획득할 수 있고,
+`UNLOCK TABLES` 명령으로 잠금을 반납할 수 있습니다.
+ +묵시적인 테이블 락은 MyISAM 또는 MEMORY 테이블에 데이터를 변경하는 쿼리를 실행할 때 자동으로 발생합니다.
+하지만 InnoDB의 경우 레코드 기반의 잠금을 제공하기 때문에 묵시적인 테이블 락이 설정되지 않습니다.
+InnoDB에서는 대부분의 DML 쿼리에서는 테이블 락이 무시되고 DDL에서만 영향을 미칩니다. + +## 3. 네임드 락(Named Lock) + +네임드 락은 `GET_LOCK()` 함수를 이용해 임의의 문자열에 대해 잠금을 설정할 수 있습니다.
+일반적인 테이블 락과 달리 특정 테이블이나 행에 종속되지 않습니다.
+ +- 네임드 락 획득: 다른 세션에 의해 잠금이 걸려있다면 최대 10초 동안 대기. 획득 성공 시 1, 실패 시 0 반환 + +```sql +SELECT GET_LOCK('mylock_1', 10); +``` + +- 네임드 락 해제: 성공 시 1, 실패 시 0, 오류 시 NULL 반환 + +```sql +SELECT RELEASE_LOCK('mylock_1'); +``` + +- 네임드 락이 현재 사용 중인지 확인: 다른 세션에 의해 잠금이 걸려있다면 그 세션 ID 반환, 아니면 NULL 반환 + +```SQL +SELECT IS_USED_LOCK('mylock_1'); +``` + +- 네임드 락이 자유로운지 확인: 어떤 세션에도 잡혀있지 않으면 1 반환, 이미 사용 중이면 0 반환 + +```sql +SELECT IS_FREE_LOCK('mylock_1'); +``` + +동일 이름의 락이 여러 세션에서 동시에 요청하면, 먼저 요청한 세션이 먼저 락을 획득합니다.
+예를 들어 배치 프로그램을 실행할 때 동시에 여러 웹서버에서 동일 데이터를 변경하고 참조하는 경우, 네임드 락을 걸고 쿼리를 실행하면 데드락을 방지할 수 있습니다.
+ +## 4. 메타데이터 락(Metadata Lock) + +메타데이터 락은 데이터베이스 객체(테이블, 뷰 등)의 이름이나 구조를 변경하는 경우 획득하는 잠금입니다.
+명시적으로 획득하는 것이 아니라, `RENAME TABLE table_a TO table_b` 같이 테이블 이름을 변경하는 경우 자동으로 획득합니다. diff --git a/docs/posts/fe-interview/seo.mdx b/docs/posts/fe-interview/seo.mdx new file mode 100644 index 0000000..cdf9485 --- /dev/null +++ b/docs/posts/fe-interview/seo.mdx @@ -0,0 +1,31 @@ +--- +title: SEO란 무엇인가요? +type: 'interview' +language: 'FE-Interview' +tags: + - SEO + - 검색 엔진 +dateModified: 2025.09.28 +--- + +## 답변 + +SEO(Search Engine Optimization)는 검색 엔진이 웹사이트와 페이지의 내용을 더 잘 이해하도록 구조와 콘텐츠를 최적화하는 작업입니다. 검색 결과 상위에 노출되어 사용자가 쉽게 정보를 찾고, 사이트 방문과 전환으로 이어지도록 만드는 것이 핵심 목적입니다. + +### Q. 검색 엔진 알고리즘에 대해 설명해 주세요. + +구글은 아래 3단계를 거쳐 SERP(Search Engine Result Page, 검색 결과 페이지)를 완성합니다: + +1. **크롤링(Crawling)**: 구글봇(웹 크롤러)이 웹 페이지의 콘텐츠를 탐색하고 복사해 정보를 수집합니다. +2. **인덱싱(Indexing)**: 수집된 정보를 주제별로 분류해 색인에 저장합니다. +3. **랭킹(Ranking)**: 색인된 콘텐츠를 사용자의 검색 의도와 연관성에 따라 순위를 매기고, 결과를 노출합니다. + +### Q. SEO의 종류에 대해 설명해 주세요. + +1. **Technical SEO**: 검색 엔진이 웹사이트를 원활하게 크롤링·분석할 수 있도록 기술적인 구조를 최적화합니다. + (예: Robots.txt, Sitemap, Schema Markup) + +2. **On-page SEO (콘텐츠 최적화)**: 검색 사용자의 의도에 맞춰 사이트 내부 콘텐츠를 개선합니다. + (예: Keyword research, h tags, meta tags, copy write) + +3. **Off-page SEO (링크 빌딩)**: 외부의 신뢰할 만한 사이트로부터 언급되거나 링크될 경우, 검색 엔진은 해당 사이트를 더 가치 있는 정보로 인식합니다. diff --git a/docs/posts/java/java-gc.mdx b/docs/posts/java/java-gc.mdx new file mode 100644 index 0000000..cc62d78 --- /dev/null +++ b/docs/posts/java/java-gc.mdx @@ -0,0 +1,65 @@ +--- +title: JAVA의 Garbage Collection 종류와 특징 +type: 'concept' +language: Java +tags: + - Java +dateModified: 2025.10.15 +--- + +# JAVA의 Garbage Collection + +JAVA에는 메모리 관리를 위한 GC가 내장되어 있습니다. +이 문서에서는 JAVA의 GC 종류와 특징에 대해 학습합니다. + +## GC란 +- 자바는 new로 객체를 만들면 메모리(heap)에 저장합니다. +- 그런데, 사용하지 않는 객체(=Garbage)를 계속 메모리에 두면 공간이 낭비됩니다. +- 그래서 GC가 주기적으로 필요 없는 객체를 찾아서 제거해줍니다. +- 이러한 GC는 방식(알고리즘)이 다양하고, 각각 특징이 있습니다. + +## 1. Serial GC (JDK 1.2 ~) +- 특징: 단일 스레드로 GC를 수행합니다. (=GC 수행 중 모든 작업이 중단됩니다.) +- 장점: 메모리 소비가 적고, 구현이 단순합니다. +- 단점: 전체 애플리케이션이 멈추기 때문에 멀티코어 환경에 비효율적입니다. +- 단일 코어, 작은 힙, 데스크탑 앱에 적합하며, 보통 실무에서는 거의 사용하지 않습니다. + +## 2. Parallel GC (JDK 1.4 ~) +- 특징: GC를 여러 스레드로 병렬 수행합니다. Java 8의 디폴트 GC입니다. +- 장점: Serial GC에 비해 Throughtput(처리량)이 높습니다. (=앱 성능 향상) +- 단점: 여전히 GC 중 애플리케이션이 중단됩니다. (=Stop-the=world가 존재합니다.) + - Paraller GC가 멀티쓰레드로 동작하는데 앱이 중단되는 이유는? + - GC가 실행될 때 애플리케이션 스레드를 멈추고 그 시간 동안 멀티스레드로 GC 작업을 수행하기 때문입니다. + - 즉, GC 작업만 멀티스레드로 빠르게 수행합니다. + - Paraller GC의 목적은 GC를 빠르게 수행하는 것에 있으며, **애플리케이션은 멈추더라도 멀티스레드로 빠르게 청소하고 앱을 다시 실행하자!** 라는 전략입니다. +- 백그라운드 작업, 배치 처리, 멀티코어 환경에 적합합니다. + +## 3. CMS(Concurrent Mark-Sweep) GC (JDK 1.5 ~) +- 특징: 일부 GC 작업을 앱 실행과 병행하여 수행합니다. +- 장점: 응답성이 높습니다. (=Strop-the-world 시간이 짧습니다.) +- 단점: Fragmentation 이 발생하며, CPU 사용량이 증가합니다. + - Fragmentation(조각화)란? + - 메모리 관리의 핵심 개념 중 하나로, 메모리를 사용하고 회수하는 과정에서 **쓸 수 있는 메모리는 많으나 연속된 공간이 부족해서 실제로는 메모리를 못 쓰는 현상**을 말합니다. +- 사용자 응답이 중요한 웹 서비스에 적합합니다. +- 메모리 조각화 현상 때문에, Java9 버전부터 deprecated 되었으며, Java14 부터는 사용이 중지되었습니다. + +## 4. G1(Garbage First) GC (JDK 7 ~) +- 특징: 힙을 여러 region으로 나누고 GC를 분산처리합니다. +- 장점: Pause time을 예측 가능하게 설정 가능합니다. (예: -XX:MaxGCPauseMillis=) + - Pause time 이란? + - GC가 실행되면서 애플리케이션의 실행이 일시 중지되는 시간을 말합니다. + - GC로 인해 STW(Stop-the-world)가 발생하며, 이때의 시간을 바로 Pause time 이라고 부릅니다. +- 단점: 설정이 복잡하고 CPU 오버헤드가 존재합니다. + - 모든 GC는 오버헤드가 있으며, 최신 GC일수록 Pause time을 줄이는 대신 내부 오버헤드가 늘어납니다. +- 대용량 힙인 경우, 응답성+처리량 둘 다 중요한 경우에 적합합니다. + +## 5. ZGC(Z Garbace Collector) (JDK 11 ~) +- 특징: 매우 짧은 GC 중단 시간을 가지며, 대부분의 작업을 병행 수행합니다. +- 장점: 큰 힙에서도 Pause time이 짧습니다. (=초대형 어플리케이션에 적합합니다.) +- 단점: 힙 사용량이 많고, 실험적 기능이 많았으나 최근 안정화되었습니다. +- 실시간 응답이 중요한 대규모 시스템(금융 트레이딩 시스템 등)에 적합합니다. + +## 6. Shenandoah GC (JDK 12 ~) +- 특징: ZCG와 유사하게 낮은 Pause time을 추구합니다. (모든 단계를 병행 수행합니다.) OpenJDK 기반이며, RedHat이 주도합니다. +- 장점: 짧은 GC 지연과, 대규모 JVM에서도 안정적입니다. +- 단점: CPU 오버헤드가 존재합니다. diff --git a/docs/posts/javascript/class.mdx b/docs/posts/javascript/class.mdx new file mode 100644 index 0000000..887efb7 --- /dev/null +++ b/docs/posts/javascript/class.mdx @@ -0,0 +1,78 @@ +--- +title: 'JavaScript의 Class와 Prototype' +type: 'concept' +language: 'Javascript' +tags: + - Javascript + - Class +dateModified: 2025.10.04 +--- + +## Javascript의 Class + +- 자바스크립트에서 클래스는 함수의 한 종류입니다. +- Class를 선언하고 `new` 키워드로 인스턴스를 생성하면 Class 내부에 선언된 변수 및 함수에 접근할 수 있습니다. + +### Javascript는 프로토타입 기반 언어 + +- 'Class' 라는 개념이 JavaScript에 도입되기 전에 생성자 함수라는 개념이 존재했습니다. + +```js +function Person() {} // 📍 통상적으로 생성자 함수는 첫 스펠링을 대문자로 작성하는 컨벤션이 있습니다. + +Person.prototype.legs = 2; +Person.prototype.mouth = 1; + +var lee = new Person(); +var sue = new Person(): + +console.log(lee.legs); // => 2 +console.log(lee.mouth); // => 1 +console.log(sue.legs); // => 2 +console.log(use.mouth); // => 1 +``` + +### Class vs 생성자 함수 +- 내부 프로퍼티는 거의 동일하지만 constructor 부분이 function과 class로 다른 점을 알 수 있습니다. +- Class로 선언해도 결국 객체의 `[[Prototype]]` 체인이 작동합니다. 사실상 이 둘의 큰 차이가 없기 때문에 Class는 문법적 설탕이라고 여겨지기도 합니다. + +| 구분 | class | function constructor | +|------|--------|-----------------------| +| 선언 방식 | `class Person2 { ... }` | `function Person() { ... }` | +| constructor 표시 | `constructor: class Person2` | `constructor: f Person()` | +| 인스턴스 생성 | `new Person2()` | `new Person()` | +| 특징 | - ES6에서 도입된 문법적 설탕 (syntactic sugar). 내부적으로는 함수 기반 prototype을 사용하지만 `class`로 표현됨. `strict mode`가 자동 적용됨 | - 전통적인 생성자 함수 방식. 명시적으로 함수 이름이 표시됨. 일반 함수와 동일한 문법 (엄격 모드 자동 적용 X) | + + +```js +/* 생성자 함수로 정의한 경우 */ +Person {} + [[Prototype]]: Object + constructor: f Person() + [[Prototype]]: Object + +/* Class로 정의한 경우 */ +Person {} + [[Prototype]]: Object + constructor: class Person + [[Prototype]]: Object +``` + +### Property Chain +- JavaScript 객체는 자신이 가진 프로퍼티를 먼저 찾고, 없으면 prototype(상위 객체) 에서 프로퍼티를 찾습니다. +- 이처럼 연결된 prototype 객체들을 따라 올라가면서 프로퍼티를 탐색하는 과정을 prototype chain이라고 부릅니다. + +#### Prototype Chain 동작 원리 + 1. 객체에 해당 프로퍼티가 있는지 확인 + 2. 없으면 [[Prototype]]을 따라 상위 객체 탐색 + 3. 계속 위로 올라가다가 Object.prototype까지 확인 + 4. 끝까지 없으면 undefined 반환 + + +📌 JavaScript는 prototype 체인을 통해 객체 간 속성과 메서드를 공유하는 prototype 기반 언어이고, ES6의 class는 이 개념을 좀 더 익숙한 클래스 문법으로 표현한 것입니다. + +| 구분 | class | function constructor | +|------|--------|-----------------------| +| 프로토타입 연결 | 인스턴스의 `[[Prototype]]` → `Person.prototype` | 인스턴스의 `[[Prototype]]` → `Person.prototype` | +| 메서드 정의 | `class` 안에서 정의한 메서드는 자동으로 `Person.prototype`에 저장됨 | 함수에 직접 `Person.prototype.method = ...`로 추가해야 함 | +| 체인 탐색 순서 | `인스턴스 → Person.prototype → Object.prototype → null` | `인스턴스 → Person.prototype → Object.prototype → null` | diff --git a/docs/posts/javascript/object-immutability.mdx b/docs/posts/javascript/object-immutability.mdx new file mode 100644 index 0000000..f3c728b --- /dev/null +++ b/docs/posts/javascript/object-immutability.mdx @@ -0,0 +1,176 @@ +--- +title: javascript에서 객체의 불변성을 유지하는 방법 +type: 'concept' +language: JavaScript +tags: + - Javascript +dateModified: 2025.10.17 +--- + +# javascript에서 객체의 불변성을 유지하는 방법 + +## 객체의 불변성이란 + +자바스크립트에서 객체는 참조형 데이터 타입으로, 불변성을 가진 원시 타입과는 달리 동일한 참조를 하고 있는 다른 객체에서도 객체 내부의 값을 변경하는 것이 가능합니다. + +객체의 불변성을 지킨다는 것은 객체의 원본 상태를 변경하지 않고, 새로운 객체를 만들어 사용하는 것을 말합니다. + +#### 객체의 불변성을 지키지 않으면 발생하는 문제 예시 + +- 전역 상태 관리에서 자주 생기는 문제 (ex: Redux) + +```js +const state = { + user: { name: 'Tom' }, +}; + +const anotherRef = state.user; +anotherRef.name = 'Jack'; // 원본 state.user도 함께 변경 + +console.log(state.user.name); // "Jack" +``` + +- React는 얕은 비교(shallow compare)를 통해 상태 변경 여부를 판단하기 때문에, 원본 객체를 직접 변경하면 참조 값이 변하지 않아 렌더링이 일어나지 않을 수 있습니다. + +```js +const [user, setUser] = useState({ name: 'Jade' }); + +user.name = 'Kim'; // 원본 수정 — 객체가 직접 변경되어 React는 변화 감지 못함 +setUser(user); + +setUser({ ...user, name: 'Kim' }); // 새로운 객체 생성 — 변화 감지됨 +``` + +위와 같은 문제 때문에 Redux에서 상태를 변경할 때는 반드시 새로운 객체를 만들어 반환해야 합니다. + +마찬가지로 React에서 상태를 직접 수정하지 않고 setState 함수를 사용하는 이유는 불변성을 유지하고 컴포넌트 렌더링을 올바르게 트리거하기 위해서입니다. + +
+ +## 객체의 불변성을 지키기 위한 방법 + +### 1. 내장 매서드 사용하기 + +#### Object.freeze + +객체의 값을 수정하거나 추가하거나 삭제할 수 없습니다. + +```js +const obj = { prop: 42 }; + +Object.freeze(obj); + +obj.prop = 33; +// Throws an error in strict mode + +console.log(obj.prop); +// Expected output: 42 +``` + +다만 Object.freeze는 얕은 동결만을 제공합니다. + +```js +obj = { + internal: {}, +}; + +Object.freeze(obj); +obj.internal.a = 'aValue'; + +obj.internal.a; // 'aValue' 로 변경 됨 +``` + +#### Object.seal + +객체에 새로운 프로퍼티를 추가하거나 삭제할 수 없지만, 기존 프로퍼티의 값을 수정할 수는 있습니다. + +#### Object.preventExtensions + +새로운 프로퍼티의 추가만 막습니다. + +### 정리 + +| 메서드 | 값 수정 가능 | 속성 추가 가능 | 속성 삭제 가능 | 구조 고정 | 깊은 동결 | +| -------------------------- | ------------ | -------------- | -------------- | --------- | --------- | +| `Object.freeze` | X | X | X | O | X | +| `Object.seal` | O | X | X | O | X | +| `Object.preventExtensions` | O | X | O | X | X | + +
+ +### 2. 깊은 수준의 객체의 불변성을 지키기 위한 방법 + +#### 재귀적으로 동결시키기 + +객체를 불변하게 만들기 위해서는, 각 객체의 속성들을 재귀적으로 동결할 수 있습니다. + +```js +function deepFreeze(obj) { + Object.getOwnPropertyNames(obj).forEach((key) => { + const value = obj[key]; + if (typeof value === 'object' && value !== null) deepFreeze(value); + }); + return Object.freeze(obj); +} + +const frozen = deepFreeze({ nested: { x: 1 } }); +frozen.nested.x = 2; +``` + +#### immer, immutable.js 등의 불변성 옵션을 제공하는 라이브러리 사용하기 + +- immer : 기존 JS 객체를 불변하게 다루는 것을 도와 주는 라이브러리 + +```js +import { produce } from 'immer'; + +const state = { user: { name: 'Jade' } }; + +const newState = produce(state, (draft) => { + draft.user.name = 'Kim'; +}); + +console.log(state.user.name); // Jade +console.log(newState.user.name); // Kim +``` + +-immutable.js : 불변 자료구조를 제공하는 라이브러리 + +\*\* 주의 : immutable.js에서 제공하는 Map 자료구조는 javascript의 Map과는 역할과 용도가 다릅니다. + +```js +import { Map } from 'immutable'; + +const user = Map({ name: 'Tom', age: 27 }); + +// 값 수정 시 원본을 변경하지 않고 새로운 객체를 반환 +const updatedUser = user.set('name', 'Jack'); + +console.log(user.get('name')); // "Tom" +console.log(updatedUser.get('name')); // "Jack" + +// 중첩 객체 +const nested = Map({ + user: Map({ + name: 'Tom', + profile: Map({ email: 'tom@example.com' }), + }), +}); + +const updatedNested = nested.setIn(['user', 'profile', 'email'], 'jack@example.com'); + +console.log(nested.getIn(['user', 'profile', 'email'])); +// 원본 객체는 변경되지 않음 : "tom@example.com" + +console.log(updatedNested.getIn(['user', 'profile', 'email'])); +// 변경된 객체 : "jack@example.com" +``` + +
+ +## 정리 + +- 자바스크립트 객체는 참조형 타입이기 때문에 한 곳에서 변경이 일어나면 다른 참조 지점에도 영향을 미칠 수 있습니다. +- 불변성을 지키면 이러한 예기치 않은 사이드 이펙트를 방지할 수 있습니다. +- 불변성을 지키는 방법: **내장 메서드 사용 / 재귀적 동결 / 라이브러리 활용(immer, Immutable.js)** +- Object.freeze는 직렬화(JSON.stringify) 자체를 막지는 않지만, 라이브러리에서 데이터를 조작하는 경우 제약이 생길 수 있으므로 상황에 맞게 사용하는 것이 좋습니다. diff --git a/public/docs-assets/architecture/leaderElection.png b/public/docs-assets/architecture/leaderElection.png new file mode 100644 index 0000000..41f4b39 Binary files /dev/null and b/public/docs-assets/architecture/leaderElection.png differ diff --git a/public/docs-assets/architecture/raftcluster.png b/public/docs-assets/architecture/raftcluster.png new file mode 100644 index 0000000..790355e Binary files /dev/null and b/public/docs-assets/architecture/raftcluster.png differ diff --git a/public/manifest.json b/public/manifest.json index 0f71482..cd17bcd 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -3,7 +3,7 @@ "name": "디룩(Delook)", "author": "@waterbinnn", "homepage_url": "https://www.delook.co.kr/", - "version": "1.0.10", + "version": "1.0.11", "description": "개념 학습부터 기술 면접 준비까지, 성장하는 개발자의 새 탭", "action": { "default_popup": "popup.html",