-
Notifications
You must be signed in to change notification settings - Fork 0
Database ‐ Merge, MRR, New Features, Hints, and Operations
woojin edited this page Aug 12, 2026
·
7 revisions
- 서로 다른 두 컬럼에 조인이 걸리고 단일 인덱스 하나로는 그중 한쪽밖에 커버하지 못할 때 두 인덱스를 동시에 활용할 수 있는 방법이 있다.
- 그 전에 이걸
UNION쿼리로 합칠 수도 있을 것이라는 생각도 하게 되는데UNION은 두 결과의 중복을 제거하고자 임시 테이블에 모아 정렬과 중복 제거를 한다. - 여기에 더해, 각 갈래는 인덱스로 찾은 행을 테이블에서 통째로 읽어와 임시 테이블에 쌓는다. 조건에 맞는 행이 많으면 많을수록 이 비용은 늘어나게 된다.
- 이 "쪼개서 인덱스 타고 합치기"를 OR로 쓴 원래 쿼리만 보고 옵티마이저가 알아서 해주는 기능이 바로 인덱스 머지다.
- 기본적으로 하나의 테이블 접근에는 하나의 인덱스만 사용된다. 그런데 예외가 있다.
- 옵티마이저가 여러 인덱스를 각각 스캔한 뒤 결과를 합치거나 교차시키는 전략을 선택하는 경우가 있다. 인덱스 머지에는 3가지 알고리즘이 있다.
| 알고리즘 | 적용 조건 | Extra 표시 |
|---|---|---|
| Intersection(교집합) | AND 조건. 각 인덱스를 동등 조건으로 사용 | Using intersect |
| Union(합집합) | OR 조건. 각 갈래가 동등 조건 | Using union |
| Sort-Union(정렬 후 합집합) | OR 조건. 범위 조건이 섞여 Union이 불가능한 경우 | Using sort_union |
- 동등 조건(=)으로 인덱스를 스캔하면 결과 row id가 PK 순서로 나오므로 그대로 합치면 된다(Union).
- 반면 범위 조건(<, >, BETWEEN)으로 스캔하면 row id가 PK 순서로 나오지 않아, 합치기 전에 먼저 정렬해야 한다(Sort-Union).
- 인덱스 머지는 옵티마이저가 비용 기반으로 선택한다.
- 그런데
SELECT *로 모든 컬럼을 읽거나 한쪽 조건이 많은 행을 매칭하면, 옵티마이저는 병합 후 랜덤 접근하느니 풀 테이블 스캔이 싸다고 판단해 인덱스 머지를 버리는 경우가 많다.
- 각 OR 갈래가 동등 조건이면, 각 인덱스 스캔 결과가 PK 순서로 나온다. 그래서 정렬 없이 바로 합칠 수 있다 → Union.
EXPLAIN SELECT /*+ INDEX_MERGE(product idx_product_status, idx_product_price) */ *
FROM product
WHERE product_status = 'SOLD_OUT' OR price = 5000;- OR 갈래에 범위 조건이 섞이면 이야기가 달라진다.
- 범위 스캔(price > 50000)은 결과 row id가 PK 순서로 나오지 않는다. 그래서 합치기 전에 먼저 정렬해야 한다 → Sort-Union.
EXPLAIN SELECT /*+ INDEX_MERGE(product idx_product_status, idx_product_price) */ *
FROM product
WHERE product_status = 'SOLD_OUT' OR price > 50000;EXPLAIN SELECT /*+ INDEX_MERGE(product idx_product_category_id, idx_product_status) */ *
FROM product
WHERE category_id = 11 AND product_status = 'ACTIVE';- 두 인덱스 각각을 스캔하고 결과의 교집합(intersection)을 구한다.
-
category_id = 11인 PK 목록과product_status = 'ACTIVE'인 PK 목록의 교집합에 해당하는 행만 테이블에서 읽는다.
- 실제로 성능 차이는 각 상황마다 다르지만 대부분의 경우 복합 인덱스 1개가 인덱스 머지보다 효율적이다.
| 비교항목 | 인덱스 머지 | 복합 인덱스 |
|---|---|---|
| 인덱스 스캔 횟수 | 2회 이상 | 1회 |
| 결과 병합에 들어가는 비용 | 있음(정렬/교차/합집합) | 없음 |
| 테이블 접근 패턴 | 병합 후 랜덤 점근 | 단일 스캔 후 접근 |
| 옵티마이저 비용 추정 | 복잡 | 단순 |
- 인덱스 머지는 각 인덱스를 따로 스캔하고 결과를 합치는 추가 비용이 든다. 복합 인덱스는 한 번의 스캔으로 모든 조건을 처리한다.
- 적절한 복합 인덱스가 없는 상황에서 인덱스 머지는 풀 테이블 스캔보다 훨씬 낫다.
- 인덱스 머지 자체가 나쁜 것이 아니다. "복합 인덱스로 대체할 수 있는지"를 먼저 검토하라는 것이 핵심이다.
-- 인덱스 머지 관련 플래그 확인
-- NO_INDEX_MERGE 는 인덱스 머지를 사용하지 않는 힌트
SELECT @@optimizer_switch;
-- index_merge=on, index_merge_intersection=on,
-- index_merge_union=on, index_merge_sort_union=on- Multi-Range Read는 단어 그대로 인덱스에서 매칭된 여러 범위의 행들을 한 번에 모아서 읽는다는 뜻이다.
- 세컨더리 인덱스의 리프에는 행 데이터가 없다. 대신 인덱스 키 값과 PK만 들어 있다. 그래서
SELECT *처럼 행 전체가 필요한 쿼리는 인덱스에서 찾은 PK를 들고 클러스터드 인덱스를 다시 한 번 탐색해야 한다. 이것이 2단 점프다. - 대량의 데이터를 읽을 때는 순차 I/O가 랜덤 I/O보다 훨씬 빠르다.
- 해결책은 간단하다. PK가 나올 때마다 곧장 테이블로 뛰어가지 말고 일단 버퍼에 모아둔다.
- 세컨더리 인덱스를 스캔해 조건에 맞는 PK들을 내부 버퍼에 모은다.
- 모인 PK들을 PK 순서로 정렬한다.
- 정렬된 PK 순서대로 테이블을 한 방향으로 훑으며 행을 읽는다.
- PK 순서로 정렬해두면 테이블 접근이 앞에서 뒤로 한 방향으로만 흐른다. 따라서 디스크가 앞뒤로 튀지 않으니 랜덤 I/O가 순차 I/O에 가까워진다.
- 게다가 이렇게 되면 한 페이지에 여러 데이터를 모아서 조회할 가능성도 높아진다.
- 읽는 페이지의 집합은 똑같은데 읽는 순서가 달라지는 것만으로 디스크 I/O 패턴이 완전히 바뀌는데 이것이 MRR의 전부라고 봐도 무방하다.
- 스토리지 엔진에서 PK 정렬이라는 한 수로 끌어다 쓰는 것이다.
-
EXPLAIN은 옵티마이저가 세운 계획이다. "MRR을 쓸 작정이다"라는 의도가 Using MRR 로 표시된다. -
EXPLAIN ANALYZE는 실행 엔진의 연산자(iterator) 트리를 보여준다. 그런데 MRR은 별도의 연산자가 아니라,Index range scan이 행을 읽어 올 때, 스토리지 엔진 내부에서 PK를 모아 정렬하는 읽기 방식일 뿐이다. 트리의 노드 단위로 드러나는 동작이 아니라서 별도로 찍히지 않는다. -
Using MRR은EXPLAIN의Extra에서 확인하는 신호고,EXPLAIN ANALYZE트리에 안 보인다고 MRR이 동작하지 않은 것은 아니다.
- MRR은 무조건 켜지는 게 아니다. 옵티마이저가 비용 기반(cost-based)으로 "MRR이 이득인가"를 따져서 쓸지 말지를 결정한다.
- MRR의 이득은 랜덤 I/O를 순차 I/O로 바꾸는 것이다. 그런데 데이터가 이미 메모리(버퍼 풀)에 올라와 있다면 애초에 디스크를 안 읽으니 랜덤 I/O의 패널티가 거의 없다. 이런 상황에서 MRR을 PK를 모아 정렬하는 비용이 더 들기 때문에 옵티마이저가 MRR을 버린다.
- MRR이 빛을 발하는 순간은 데이터가 버퍼 풀에 다 안 들어가서 디스크에서 읽어야 하는, 대량의 범위 스캔이다.
- MRR 동작은
optimizer_switch로 제어한다.
SELECT @@optimizer_switch;
-- ... mrr=on, mrr_cost_based=on ...-
mrr: MRR 기능 자체를 켜고 끈다.(기본값 on) -
mrr_cost_based: MRR을 비용 기반으로 판단할지 여부다.(기본값 on)on이면 위에서 본 대로 이득일 때만 쓴다.
-- 비용 판단을 끄고 MRR을 적극적으로 사용 (관찰/디버깅용)
SET optimizer_switch = 'mrr=on,mrr_cost_based=off';- PK를 모아 정렬하는 버퍼의 크기는
read_rnd_buffer_size(기본값 256KB)로 정한다. 이 버퍼가 가득 차면 모인 만큼 정렬해서 읽고, 다시 채우기를 반복한다. - 버퍼가 클수록 한 번에 더 많은 PK를 정렬해 순차성이 좋아지지만, 세션마다 잡히는 메모리이므로 동시 접속이 많은 환경에서 무작정 키우면 메모리 사용량도 불어난다.
-
mrr_cost_based=off로 강제하더라도 커버링 인덱스라서 테이블 접근이 없거나 결과가 극소수라면 MRR이 의미가 없어 나타나지 않을 수 있다. MRR은 어디까지나 세컨더리 인덱스로 테이블 행을 다량 읽어야 할 때 쓰는 도구이다.
-- 원상 복귀
SET optimizer_switch = 'mrr=on,mrr_cost_based=on';EXPLAIN SELECT *
FROM member
WHERE LOWER(email) = 'user100@shop.com';-
member테이블에uk_member_emailUNIQUE 인덱스가 있음에도 불구하고 풀 테이블 스캔이 된다. - 그 이유는
LOWER(email)이라는 함수를 적용했기 때문이다. 인덱스에는email이라는 원본 값이 저장되어 있는데, 쿼리는LOWER(email)결과를 비교한다. 따라서 인덱스의 정렬 구조를 활용할 수 없다. - WHERE절에서 컬럼에 함수를 적용하면 인덱스를 탈 수 없다. 이럴 경우 MySQL 8.0.13부터 제공하는 함수 기반 인덱스(Function Index)를 사용하면 컬럼을 가공해도 인덱스를 태울 수 있다.
-- 함수 기반 인덱스 생성
CREATE INDEX idx_member_lower_email ON member ((LOWER(email)));- 괄호가 이중으로 감싸져 있는데 바깥쪽 괄호는 인덱스 컬럼의 목록, 안쪽 괄호는 표현식을 나타낸다.
- 함수 기반 인덱스는 내부적으로 숨겨진 가상 생성 컬럼(hidden virtual generated column)으로 구현된다.
- MySQL의
LOWER(email)결과를 계산해 별도의 가상 컬럼에 저장하고, 그 컬럼에 인덱스를 건다. 쿼리에서LOWER(email)을 사용하면 옵티마이저가 이 가상 컬럼의 인덱스를 자동으로 매칭한다.
- 함수의 계산 결과를 인덱스가 들고 있으니, 그 결과를 조회하는 쿼리는 인덱스만으로 끝낼 수 있다.
- PK에는 함수 기반 키 파트를 포함할 수 없다.(가상 컬럼은 PK가 될 수 없다)
- 생성 컬럼에 허용되는 함수만 사용할 수 있다.(서브쿼리, 저장 프로시저 등은 불가하다)
- 각 함수 기반 키 파트는 테이블의 전체 컬럼 수 제한에 포함된다.
- 함수 기반 인덱스가 유용한 대표적인 경우는 대소문자 무시 검색, 날짜에서 연/월 추출 검색, JSON 값 추출 검색 등이 있다.
- 운영 환경에서 인덱스를 잘못 삭제해서 중요한 쿼리가 느려지면 대참사가 발생한다. 다시 인덱스를 만들려면 수십 분이 걸릴 수도 있다.
- 그 사이 사용자 요청은 계속 느려져서 인덱스를 다시 만들 때까지 장애가 발생한다.
- 그렇다고 아무것도 하지 않으면 사용하지 않는 인덱스가 계속 남아 쓰기 성능과 디스크 공간을 잡아먹는다. 운영에서는 삭제 전 영향도를 안전하게 검증하는 방법이 필요하다.
Invisible Index
- Invisible Index는 인덱스를 물리적으로 삭제하지 않고, 옵티마이저에서만 안 보이게 만드는 기능이다.
-- idx_product_category_id 인덱스를 INVISIBLE로 변경
ALTER TABLE product ALTER INDEX idx_product_category_id INVISIBLE;- 해당 명령은 빠른 인플레이스 작업이라 테이블을 재구성하지 않는다. 인덱스는 물리적으로 그대로 존재하고 INSERT/UPDATE/DELETE시 계속 유지된다. 다만 옵티마이저가 실행 계획을 세울 때 이 인덱스를 무시한다.
- PK는 Invisible로 만들 수 없다. PK가 없는 테이블에서 암묵적 PK 역할을 하는 UNIQUE 인덱스도 마찬가지다.
- Invisible Index도 INSERT/UPDATE/DELETE시 유지 비용이 발생한다. 쓰기 성능에 미치는 영향은 Visible과 동일하다.
-
optimizer_switch의use_invisible_indexes플래그를on으로 설정하면 Invisible 인덱스도 옵티마이저가 사용한다. 기본값은off이다.
운영 절차 : 인덱스를 제거할 때는 보통 다음 순서로 진행하는 것을 권장한다.
- 삭제 후보 인덱스를 바로
DROP하지 말고 먼저INVISIBLE로 바꾼다.- 대표 쿼리 실행 계획과 응답 시간을 확인한다.
- 운영 트래픽을 일정 시간 모니터링한다.
- 문제가 생기면 즉시
INVISIBLE로 원복한다.- 문제가 없으면 그 때,
DROP INDEX로 실제 삭제한다.
- MySQL 8.0부터 인덱스 정의에서
DESC를 명시적으로 지정할 수 있다. -
Descending Index는 혼합 정렬 요구사항에서 빛을 발한다.
-- 카테고리별로 가격이 높은 상품부터 정렬하는 기능이 필요하다는 요구사항 가정
EXPLAIN SELECT product_id, category_id, price
FROM product
ORDER BY category_id ASC, price DESC
LIMIT 20;- 인덱스를 잘 설계했음에도 불구하고 옵티마이저가 원하는 실행 계획을 선택하지 않을 때가 있다.
- 예를 들어, 특정 인덱스가 명백히 유리한데 다른 인덱스를 선택한다거나, 반대로 특정 인덱스를 사용하면 성능이 나빠지는 경우가 있다.
- 이럴 때, 실행 계획에 개입하는 도구가 바로 힌트다. 과거에는
USE INDEX,FORCE INDEX,IGNORE INDEX와 같은 인덱스 힌트를 많이 사용했다. 하지만 MySQL의 앞으로의 방향은/*+ ... */형태의 옵티마이저 힌트다. - 공식 문서 기준으로는
USE INDEX,FORCE INDEX,IGNORE INDEX가 향후 MySQL에서 deprecated 되고, 이후 제거될 수 있다고 나와있다. 반대로 MySQL 8.0.20부터 지원되는INDEX(),NO_INDEX()와 같은 인덱스 레벨 옵티마이저 힌트는 이들을 대체하기 위한 문법으로 제공된다.
- 옵티마이저 힌트는
/*+ ... */형태의 특수 주석으로 작성한다.SELECT,UPDATE,DELETE등의 키워드 바로 다음 뒤에 위치한다.
--
기본 구문
SELECT /*+ 힌트이름(대상) */ 컬럼 FROM 테이블 WHERE 조건;
-- 여러 힌트를 하나의 주석에 작성
SELECT /*+ INDEX(t1 idx1) JOIN_ORDER(t1, t2) */ ...- 이 때, 주의할 점이 있다. 힌트 주석은 하나만 쓸 수 있다. 2개의 힌트 주석을 쓰면 두 번째는 무시된다. 여러 힌트는 모두 하나 안에 넣어야 한다.
INDEX() : 특정 인덱스 사용 강제
- INDEX() 힌트는 지정한 인덱스를 사용하도록 강하게 유도한다. 기존
FORCE INDEX와 동등한 의미다.
EXPLAIN SELECT /*+ INDEX(product idx_productcatestatus___price) */ *
FROM product
WHERE category_id = 11;- 힌트는
SELECT바로 뒤의/*+ ... */주석 안에 작성한다.
NO_INDEX() : 특정 인덱스 배제
-
NO_INDEX()는 지정한 인덱스를 사용하지 못하게 한다. 기존IGNORE INDEX와 동등한 의미다.
EXPLAIN SELECT /*+ NO_INDEX(product idx_product_cate_status_price) */ *
FROM product
WHERE category_id = 11
AND product_status = 'ACTIVE'
AND price = 10000;- 조인 쿼리에서 옵티마이저가 선택한 테이블 순서가 비효율적인 경우 사용한다.
-- JOIN_ORDER: 조인 순서 강제
EXPLAIN SELECT /*+ JOIN_ORDER(p, oi, o) */ o.order_id, p.product_name
FROM orders o
JOIN order_item oi ON o.order_id = oi.order_id
JOIN product p ON oi.product_id = p.product_id
WHERE o.member_id = 2800000;-
JOIN_ORDER로 조인 순서를 강제할 수 있다. - 잘못된 순서를 강제하면 오히려 성능이 악화된다.
- 조인 순서 힌트에는 다음과 같은 다른 변형도 있다.
| 힌트 | 동작 |
|---|---|
| JOIN_FIXED_ORDER | FROM절에 나열한 순서대로 조인(STRAIGHT_JOIN과 동일) |
| JOIN_ORDER(t1, t2, t3) | 지정한 순서로 조인, 나머지는 옵티마이저가 결정 |
| JOIN_PREFIX(t1) | 지정한 테이블을 조인 순서 앞쪽에 배치 |
| JOIN_SUFFIX(t1) | 지정한 테이블을 조인 순서 뒤쪽에 배치 |
-
INDEX()와NO_INDEX()는 조인 정렬, 그룹화 전체 범위에 적용된다. 더 좁은 범위만을 제어하고 싶다면 용도별 힌트를 사용하면 된다.
| 옵티마이저 힌트 | 의미 | 기존 인덱스 힌트 대응 |
|---|---|---|
JOIN_INDEX(t i) |
행을 찾거나 조인할 때 인덱스 i 사용 강제 |
FORCE INDEX FOR JOIN |
ORDER_INDEX(t i) |
정렬에 인덱스 i 사용 강제 |
FORCE INDEX FOR ORDER BY |
GROUP_INDEX(t i) |
그룹화에 인덱스 i 사용 강제 |
FORCE INDEX FOR GROUP BY |
NO_JOIN_INDEX(t i) |
행 조회/조인에서 인덱스 i 배제 |
IGNORE INDEX FOR JOIN |
NO_ORDER_INDEX(t i) |
정렬에서 인덱스 i 배제 |
IGNORE INDEX FOR ORDER BY |
NO_GROUP_INDEX(t i) |
그룹화에서 인덱스 i 배제 |
IGNORE INDEX FOR GROUP BY |
대량 데이터를 읽을 때는 순차 I/O가 랜덤 I/O보다 빠를 수 있다.
INDEX()로 판단을 무시해버리면 오히려 성능이 나빠진다.
- 정리하자면, 새로 작성하는 쿼리에서는 옵티마이저 힌트를 우선 사용한다. 다만 기존 코드에서
USE INDEX를 발견했다면 단순히INDEX()로 바꾸면 안 된다.USE INDEX는 풀 스캔을 허용하는 약한 힌트이고,INDEX()는FORCE INDEX와 동등한 강한 힌트이기 때문이다.
- 인덱스를 얼마나 만들어야 하는가가 상당히 중요한 포인트이다.
- 인덱스가 많으면 SELECT가 빨라진다는 것은 이미 알고 있다. 다만 세상에 공짜는 없다.
- 인덱스는 SELECT를 빠르게 만들어주는 대신, INSERT/UPDATE/DELETE를 느리게 한다. 인덱스는 별도의 B+Tree 구조를 가진다. 인덱스를 하나 추가하면 B+Tree가 하나 더 생긴다. 데이터를 삽입할 때, 테이블(클러스터드 인덱스)뿐 아니라, 모든 세컨더리 인덱스의 B+Tree도 갱신해야 한다. 인덱스가 N개면 B+Tree 갱신이 N번 발생한다.
- 게다가 각 B+Tree에서 페이지 분할이 발생할 수 있다. 인덱스가 많을수록 페이지 분할 확률도 높아진다.
| 비용 | 설명 |
|---|---|
| 쓰기 성능 저하 | INSERT/UPDATE/DELETE마다 인덱스 B+Tree 갱신 |
| 디스크 공간 증가 | 각 인덱스가 별도의 B+Tree로 저장 |
| 페이지 분할 빈도 증가 | 인덱스별로 독립적으로 페이지 분할 발생 |
- 왜 인덱스가 많아지면 INSERT가 느려지는지 그 과정을 정리하면 다음과 같다.
| 단계 | 인덱스 0개 | 인덱스 5개 |
|---|---|---|
| 클러스터드 인덱스(PK) B+Tree 갱신 | 1회 | 1회 |
| 세컨더리 인덱스 B+Tree 갱신 | 0회 | 5회 |
| 잠재적 페이지 분할 | PK만 | PK + 5개 인덱스 모두 |
- 결과적으로 인덱스가 많을수록 쓰기 성능은 그에 비례해 저하된다.
❗읽기 vs 쓰기 비율에 따른 전략 선택이 필요 : 인덱스를 얼마나 만들지는 서비스의 읽기/쓰기 비율에 따라 달라진다
| 서비스 유형 | 읽기/쓰기 비율 | 인덱스 전략 |
|---|---|---|
| 조회 위주(쇼핑몰 상품 검색, 게시판 목록) | 읽기 90%, 쓰기 10% | 인덱스를 적극적으로 추가해도 좋다. 읽기 성능 이익이 쓰기 비용을 압도한다. |
| 균형(일반적인 CRUD 서비스) | 읽기 : 60%, 쓰기 40% | 자주 사용되는 쿼리 패턴에 집중해 인덱스를 설계한다. |
| 쓰기 위주(로그 수집, 실시간 센서 데이터, 대량 배치) | 읽기 20%, 쓰기 80% | 인덱스를 최소화한다. PK와 꼭 필요한 인덱스만 유지한다. |
추가 케이스
- 이 쿼리가 자주 실행되는가? - 하루에 1번 실행되는 쿼리를 위해 인덱스를 추가하는 것은 비효율적이다.
- 기존 인덱스로 커버할 수 없는가? - 기존 복합 인덱스 좌측 접두사로 해결되지 않는지 먼저 확인한다.
- 쓰기 비용을 감수할 만큼 읽기 이익이 큰가? - 인덱스 추가로 INSERT가 느려져도 괜찮은가?
- 인덱스 컬럼 순서가 최적인가? - 동등 ⭢ 범위 ⭢ 정렬 원칙을 따르는가?
제거 케이스
- 이 인덱스를 사용하는 쿼리가 있는가? - 실제 쿼리 사용 여부를 확인한다.
- 다른 인덱스로 대체 가능한가? - 복합 인덱스가 단일 인덱스의 역할을 포함하는지 확인한다.
- 안전하게 검증했는가? - Invisible Index로 먼저 비활성화하고 영향을 확인한다.
중복 인덱스 케이스
- 복합 인덱스
(a, b, c)가 있으면 단일 인덱스(a)는 중복이다. 복합 인덱스 좌측 접두사로(a)검색이 커버되기 때문이다. 이런 중복 인덱스는 쓰기 비용만을 늘리고 읽기 이익은 없다.
- InnoDB는 세컨더리 인덱스를 만들 때 기존 데이터를 한 건씩 B+Tree에 끼워 넣는 방식만 사용하지 않는다. 일반적인 인덱스 생성 과정은 다음 흐름으로 진행된다.
- 클러스터드 인덱스, 즉 테이블 데이터를 스캔한다.
- 새 인덱스에 필요한 키 값을 임시 정렬 파일에 기록한다.
- 인덱스 키 순서로 정렬한다.
- 정렬된 데이터를 새 세컨더리 인덱스에 적재한다.
- 이러한 과정을 Sorted Index Build라고 한다.
- 이 방식의 장점은 기존의 한 건씩 삽입하는 방식보다 B+Tree에서 삽입 위치를 매번 찾는 비용이 줄고, 페이지가 가득 찰 때마다 반복적으로 쪼개고 합치는 비용도 줄어든다는 것이다. 그래서 대량 데이터가 이미 들어있는 테이블에 인덱스를 추가할 때 더 효율적이다.
- Java - Class
- Java - Java Memory
- Java ‐ Solving Concurrency Issues with Synchronized
- Java - synchronized
- Java ‐ Instance Variable & Local Variable vs final
- Java ‐ Object
- Java ‐ Immutable Object
- Java ‐ String
- Java ‐ Wrapper Class
- Java ‐ ENUM
- Java ‐ Nested Class & Inner Class & Local Class & Anonymous Class
- Java ‐ Generic
- Java ‐ ArrayList
- Java ‐ LinkedList
- Java ‐ List
- Java ‐ Set
- Java ‐ Hash
- Java ‐ HashSet
- Java ‐ Map & Stack & Queue
- Java ‐ Iterate & Sort
- Java - Process & Thread
- Java - Thread Creation & Execution
- Java - Thread Control & LifeCycle
- Java - Memory Visibility
- Java - Advanced Synchronization
- Java - Producer/Consumer Problem
- Java - Synchronization & Atomic Operation
- Java - Concurrent Collection
- Java - Thread Pool & Executor Framework
- Java - Character Encoding
- Java - I/O
- Java - File & Files
- Java - Reflection
- Java - Annotation
- Java - Lambda
- Java - Functional Interface
- Java - Lambda vs Anonymous Class
- Java - Method Reference
- Java - Stream API
- Java - Optional
- Java - Default Method
- Java - Parallel Stream
- Java - Functional Programming
- Java - JVM & GC & SOLID
- Java - Data Storage and Memory Allocation: Primitive vs. Reference
- Java ‐ Static Keyword: Efficient Resource Management at the Class Level
- Java ‐ OOP
- Java ‐ Collection Framework Selection Standard
- Java ‐ Multi Threading & Concurrent Programming
- Java ‐ Exception Handling & Advanced Java
- Java ‐ Java 8+
- Java ‐ Java Application Performance Tuning
- Java - CAS
- Java - Virtual Thread
- Java - Benchmark
- Kotlin - Variables, Types, and Operators in Kotlin
- Kotlin - Control Flow in Kotlin
- Kotlin - Object-Oriented Programming in Kotlin
- Kotlin - Functional Programming in Kotlin
- Kotlin - Key Features of Kotlin
- Kotlin - Generics in Kotlin
- Kotlin - Lazy Initialization and Delegation in Kotlin
- Kotlin - Advanced Functional Programming in Kotlin
- Kotlin - Operator Overloading and Kotlin DSL
- Kotlin - Annotations and Reflection in Kotlin
- Kotlin - Miscellaneous Topics in Kotlin
- Coroutine - Limitations of Thread-Based Work & the Emergence of Coroutines
- Coroutine - runBlocking
- Coroutine - CoroutineDispatcher
- Coroutine - Controlling Coroutines with Job
- Coroutine - Receiving Results from Coroutines
- Coroutine - Coroutine Context
- Coroutine - Structured Concurrency
- Coroutine - Exception Handling
- Coroutine - Suspended Functions
- Coroutine - Understanding Coroutines
- Coroutine - Advanced Coroutines
- Coroutine - Coroutine Testing
- Spring - OOP & Spring
- Spring - Spring Container & Spring Bean
- Spring - Singleton Container
- Spring - Dependency Injection
- Spring - Bean LifeCycle Callback
- Spring - Bean Scope
- Spring ‐ Web Server, Web Application server
- Spring ‐ Servlet
- Spring ‐ Servlet & JSP & MVC
- Spring ‐ MVC Framework
- Spring ‐ Spring MVC
- Spring - Thymeleaf
- Spring - Message & Internationalization
- Spring - Validation
- Spring - Bean Validation
- Spring - Cookie & Session
- Spring - Filter & Interceptor
- Spring - API Exception Handling
- Spring - Spring Type Converter
- Spring - File Upload
- Spring - Connection Pool & DataSource
- Spring - Transaction
- Spring ‐ Spring Exception Abstraction
- Spring - Database Access
- Spring ‐ Spring Transaction
- Spring ‐ Spring Transaction Propagation
- Spring ‐ Thread Local
- Spring ‐ Template Method Pattern & Callback Pattern
- Spring ‐ Dynamic Proxy
- Spring ‐ Spring Proxy
- Spring ‐ Bean Processor
- Spring ‐ @Aspect AOP
- Spring ‐ Spring AOP
- Spring ‐ Spring AOP Application
- Spring - MyBatis
- Spring ‐ URL Encoding
- Spring - Cache Annotation
- Spring - Retry
- Spring Security - Initialization
- Spring Security - Authentication Process
- Spring Security - Authentication Architecture
- Spring Security - Authentication Status Persistence Processing Mechanism
- Spring Security - Session Management
- Spring Security - Exception Handling
- Spring Security - Mechanisms for responding to Malicious Attacks
- Spring Security - Authorization Process
- Spring Security - Authorization Architecture
- Spring Security - Multiple Security Settings
- Spring Security - Redis Redundancy Settings
- Spring Security - Event
- Spring Security - Integration
- Spring Security - OAuth 2.0
- Spring Security - OAuth 2.0 Authorization Type
- Spring Security - Open ID Connect
- Spring Security - OAuth 2.0 Client
- Spring Security - OAuth 2.0 Client Fundamentals
- Spring Security - OAuth 2.0 oauth2Login
- Spring Security - OAuth 2.0 oauth2Client
- Spring Security - OAuth 2.0 Resource Server
- Spring Security - OAuth 2.0 Resource Server API
- Spring Security - OAuth 2.0 Verification
- [🔖Spring Security - OAuth 2.0 MAC & RSA Token Verification]
- Spring Security - OAuth 2.0 Resource Server Permission Implementation
- Spring Security - OAuth 2.0 opaque()
- Spring Security - Integrating OAuth 2.0 Client and Resource Server
- Spring Security - Authorization Server
- Spring Security - Authorization Server Main Domain Class
- Spring Security - Authorization Server Endpoint Protocol
- Spring Batch - Scheduler vs Batch
- Spring Batch - Batch Concept
- Spring Batch - Batch Domain
- Spring Batch - Job
- Spring Batch - Step
- Spring Batch - Flow
- Spring Batch - Chunk Process
- Spring Batch - ItemReader
- Spring Batch - ItemWriter
- Spring Batch - ItemProcessor
- Spring Batch - Retry & Error Handling
- Spring Batch - Multi Threads Processing
- [Spring Batch - Batch Event Listener]
- [Spring Batch - Batch Test]
- [Spring Batch - File Processing]
- [Spring Batch - Read and Write Operations in Relational Databases and NoSQL]
- [Spring Batch - FaultTolerant & ItemStream]
- [Spring Batch - Partitioning]
- Spring Batch ‐ Tasklet vs Chunk
- [Spring Batch - Class 2]
- [Spring Batch - Class 3]
- [Spring Batch - Class 4]
- [Spring Batch - Class 5]
- [Spring Batch - Class 6]
- [Spring Batch - Class 7]
- Database - Database Introduction
- Database - Search & Sort
- Database - Data Processing
- Database - Aggregation & Grouping
- Database - Inner Join
- Database - Outer Join & Etc Join
- Database - Sub Query
- Database - UNION
- Database - CASE
- Database - Index
- Database - Data Integrity
- Database - Transaction
- Database - Why Database Design Matters
- Database - Concept Modeling
- Database - Logical Data Modeling
- Database - Identifying Relationship & Non-Identifying Relationship
- Database - Normalization
- Database - Physical Data Modeling
- Database - Common Code Design
- Database - Hierarchical Structure Design
- Database - Data Change History Design
- Database - SOFT DELETE
- Database - Statistics Table Design
- Database - Inheritance Relationship Design
- Database - Entity-Attribute-Value (EAV) Model
- Database - JSON Schema Design
- Database - Database Performance and MySQL Architecture
- Database - Execution Plans 1 - Mastering EXPLAIN
- Database - Execution Plans 2 - Mastering ANALYZE
- Database - Diagnosing Full Table Scans and Composite Index Basics
- Database ‐ Index Internal Structures 1 ‐ B-Tree & B+Tree
- Database - Index Internal Structures 2 - Clustered and Secondary Indexes
- Database - Diagnosing Using filesort and Early Termination
- Database - ICP, Covering, and Skip Scans
- Database - Merge, MRR, New Features, Hints, and Operations
- 🔖Database - The Optimizer and Histograms
- Database - Join Optimization
- Database - Optimizing Sort, Pagination, and Grouping
- Database - Streaming Aggregation and Pre-aggregated Tables
- MySQL ‐ Solving Concurrency Problems using Database-Level Locking
- MySQL - Multi Column Index
- MySQL - Covering Index & RDB vs ElasticSearch Diff
- MySQL - ORDER BY
- MySQL - INSERT
- MySQL - AUTO_INCREMENT_LOCK
- MySQL - Index Dive Using In Query
- MySQL - Why don't use prefix index in default
- MySQL - MySQL LockType
- MySQL - DeadLock Case
- MySQL - NoOffset For Query Tuning
- MySQL ‐ Skip Locked for Session
- MySQL - Checking DB Metrics with SQL Queries
- MySQL - Data Modeling for Practical Service Development
- MySQL - Basic CRUD in MySQL
- MySQL ‐ MySQL Internal Architecture and Storage Engines
- MySQL - MySQL Horizontal Scaling
- MySQL ‐ Operational‐Level System Design
- MySQL - MySQL Fundamentals
- MySQL - Why You Should Use MySQL: JOIN
- MySQL - Must-Know SQL Anti-Patterns
- MySQL - Learning Data Modeling Through Practical Examples
- MySQL - Foreign Key & Strategic Patterns
- MySQL - Advanced Topics in MySQL
- Redis ‐ Redis
- Redis ‐ Redis Manual
- Redis ‐ Redis Cache Strategy
- Redis ‐ Redis Master-Slave
- Redis - Redis Cluster Mode
- Redis - Redis Cluster Example
- Redis - Redis Data Structure
- Redis - Redis Server
- Redis - Reduce DB write load using Redis
- Redis - Solving Concurrency Issues (1)
- Redis ‐ Solving Concurrency Issues (2)
- Redis - Solving Concurrency Issues (3)
- Redis - Implementing Popular Searches
- Redis - API Rate Limiting
- Redis - Geospatial
- Redis - DAU Counting Application
- Redis - Session Management Application
- Redis - Redis Transaction ACID
- Redis - Redis Data Persistence
- Redis - Redis Keys Management
- Redis - Decoupling microservices with Redis Pub/Sub
- Redis - Redis Pipelining & RTT(Round Trip Time)
- Redis - Redis Streams
- Redis - Hash Slot Rebalancing
- JPA - Java Persistence API
- JPA - Entity Mapping & PK Strategy
- JPA ‐ JPA Association Mapping
- JPA - Proxy Association
- JPA - Value Type
- JPA - Dirty Checking vs. Merge: Understanding the Difference in JPA
- JPA - Cascading and Orphan Removal in JPA
- JPA - Introduction to Object-Oriented Query Languages in JPA
- JPA - Spring Data JPA
- JPA ‐ Solving Concurrency Issues with Pessimistic Locking
- JPA ‐ Solving Concurrency Issues with Optimistic Locking
- JPA - Lazy Loading and Performance Optimization in JPA
- JPA - ManyToOne Important Things
- JPA - OneToMany Important Things
- JPA - OSIV
- MicroService Architecture - Service Communications Patterns(RESTful API)
- MicroService Architecture - Service Communications Patterns(GraphQL)
- MicroService Architecture - Service Communications Patterns(gRPC)
- MicroService Architecture - API Gateway Patterns
- MicroService Architecture - Asynchronous Communications Patterns
- MicroService Architecture - Data Management Patterns
- MicroService Architecture - CQRS Patterns
- MicroService Architecture - Distributed Transactions
- [MicroService Architecture - Event-Driven Architecture]
- [MicroService Architecture - Resilience & Observability and Monitoring]
- [MicroService Architecture - Security Patterns]
- [MicroService Architecture - Testing Strategies]
- [MicroService Architecture - Scalability & Caching Patterns]
- [MicroService Architecture - Deployment Patterns]
- [MicroService Architecture - Serverless Architecture]
- [MicroService Architecture - GraphQL]
- [MicroService Architecture - Evolution of Distributed Systems and Their Drawbacks]
- [MicroService Architecture - Protocol Buffers]
- [MicroService Architecture - gRPC Communication Patterns]
- [MicroService Architecture - gRPC Optimization Strategies and Implementation]
- MicroService Architecture - 2PC
- MicroService Architecture - TCC
- MicroService Architecture - SAGA
- Apache Kafka - Kafka Introduction
- Apache Kafka - Kafka CLI
- Apache Kafka - Kafka Producer Application
- Apache Kafka - Kafka Consumer Application
- Apache Kafka - Idempotent Producer & Transactional Producer & Transactional Consumer
- Apache Kafka - Kafka Streams
- Apache Kafka - Kafka Connect
- Apache Kafka - Kafka Topic/Producer/Consumer
- Apache Kafka - Producer Mechanism
- Apache Kafka - Consumer Mechanism
- Apache Kafka - Multi Node Kafka Cluster
- Apache Kafka - Producer & Consumer Serialization/DeSerialization
- Apache Kafka - Topic Segment Management
- Apache Kafka - KSQLDB Stream
- Apache Kafka - KSQLDB Table
- Apache Kafka - KSQLDB Application
- Apache Kafka - Group by & Mview
- [Apache Kafka - Join]
- [Apache Kafka - Time & Windows]
- [Apache Kafka - Connecting KSQLDB to Kafka Connect]
- [Apache Kafka - Kafka Connect]
- [Apache Kafka - JDBC Source Connector]
- [Apache Kafka - JDBC Sink Connector]
- [Apache Kafka - Debezium MySQL CDC Source Connector]
- [Apache Kafka - Schema Registry]
- Apache Kafka - Differences Between RocksDB and In-Memory KeyValueStore in GlobalKTable
- Apache Kafka - Kafka Streams
- [Apache Kafka - Kafka Connect]
- [Apache Kafka - Idempotent Producers and Transactional Producers & Consumers]
- [Apache Kafka - CDC(Change Data Capture)]
- [Apache Flink - Apache Flink Architecture]
- [Apache Flink - Stream Processing]
- [Apache Flink - Data Stream API & Window]
- [Apache Flink - State Management]
- HTTP - Internet Network
- HTTP - URI & Browser Request Flow
- HTTP - HTTP Basic
- HTTP - HTTP Method
- HTTP - HTTP Method Application
- HTTP - HTTP Status Code
- HTTP ‐ HTTP Default Header
- HTTP - HTTP Cache & Condition Request
- AWS - AWS CDK(Cloud Development Kit)
- AWS - Signed URL
- AWS - PreSigned URL
- AWS - Cognito
- AWS - Signed URL Logic
- Docker - Docker
- Docker ‐ Docker CLI
- Docker ‐ Docker Volume
- Docker - Dockerfile Image
- Docker ‐ Docker Compose Container Management
- Docker ‐ Deploy(feat. AWS ECR)
- Docker - Cloud Native Technology
- Docker - Docker Essentials(1)
- Docker - Docker Essentials(2)
- Docker - Docker Network and Storage
- Docker - Building and Managing Containerized Application
- Docker - Container Orchestration
- Docker - Docker Security
- Docker - Logging and Monitoring
- Docker - Advanced Docker Usage
- Docker - Container-to-Container Communication
- [Docker - Docker Image Layer를 활용한 Cache 패턴과 Dangling Image]
- [Docker - Dockerfile 최적화를 위한 빌드 캐싱 및 멀티 스테이지 빌드 패턴]
- [Docker - GitHub Container Registry(GHCR)]
- [Docker - Docker Network 3가지]
- Kubernetes - Probe
- Kubernetes - ConfigMap & Secret
- Kubernetes - PV/PVC & Deployment & Service & HPA
- Kubernetes - Helm & Kustomize
- Kubernetes - Pod 1
- [Kubernetes - Pod 2]
- Kubernetes - Controller 1
- [Kubernetes - Controller 2]
- [Kubernetes - Object]
- [Kubernetes - Ingress & Nginx Application]
- [Kubernetes - Node Scheduling]
- [Kubernetes - Monitoring]
- [Kubernetes - Logging]
- Kubernetes - Deployment using Amazon EKS
- Jenkins - Jenkins Fundamentals & Environment Setup
- Jenkins - Jenkins Jobs: Freestyle & Pipeline
- Jenkins - Jenkins Pipeline Project
- Jenkins - Implementing Continuous Integration(CI) with Jenkins
- Nginx ‐ Nginx Introduction
- Nginx ‐ Nginx Supplementary Summary
- Nginx ‐ Deploying Domain with Nginx
- Nginx ‐ Implementing HTTPS with Nginx
- Nginx ‐ Backend Deployment via Nginx Reverse Proxy
- Nginx ‐ Load Balancing with Nginx
- Nginx - Core Concept
- [Nginx - Advanced Concept]
- [Nginx - Advanced Reverse Proxy]
- Monitoring - Log Level & Filter
- Monitoring - Log Collection with ELK Stack
- Monitoring - Log Monitoring with Kibana
- Monitoring - Server Monitoring with Prometheus and Grafana with Discord Alerts
- Test - Load Testing Fundamentals
- Test - Identifying Bottlenecks with Load Testing
- [Test - Resolving Bottlenecks and Improving Performance]
- Test - JUnit5
- Test - Mockito
- Test - TestContainers
- Test - JMeter
- Test - Chaos Monkey
- Test - ArchUnit
- Test - Appendix: Tips for Better Testing
- [gRPC - Writing .proto Files with Protocol Buffers]
- [gRPC - Various Communication Patterns in gRPC]
- [gRPC - gRPC Optimization Techniques and Advanced Features]
- TDD - Introduction to TDD
- TDD - Iterating with TDD
- TDD - Managing System Data
- TDD - Handling Unexpected Test Failures
- TDD - Abstraction & Reusability
- TDD - Building a Testing DSL
- TDD - Managing Test Context
- TDD - Driving Input Code with Output Tests
- TDD - Pagination Test
- [PostgreSQL - Docker만을 사용하는 경량화된 환경 구성 방법]
- [PostgreSQL - PostgreSQL에서 제공하는 데이터 타입]
- [PostgreSQL - PostgreSQI의 JSONB, 역인덱싱과 활용 방법]
- [PostgreSQL - 데이터베이스 성능을 위한 최적화 패턴 및 전략]
- [PostgreSQL - 트랜잭션과 ACID, Isolation 수준별 차이]
- [PostgreSQL - Database Lock 교착상태와 읽기/쓰기 성능을 보장하는 MVCC 모델]
- [PostgreSQL - pgvector와 벡터 저장, 유사도 검색 패턴 개념]
- [PostgreSQL - 벡터 인덱스 최적화와 벡터 검색과 전문 검색 결합 패턴]
- [PostgreSQL - PostgreSQL 플러그인]
- [PostgreSQL - PostGIS - 공간 쿼리와 GIST 인덱스, 지리 타입과 공간 쿼리를 위한 타입과 기본 함수]
- [PostgreSQL - pg_search - 검색 엔진 없이 텍스트 검색 구현과 주의사항]
- [PostgreSQL - 단일 인스턴스 한계를 극복하는 분산 패턴과 스케줄링, 분산 환경 구축 방법]
- [PostgreSQL - Citus - 분산 테이블과 분산 쿼리를 위한 Extension과 데이터 분산 처리]
- [PostgreSQL - pg_cron - PostgreSQL로 구성하는 CronJob]
- [PostgreSQL - 스케줄러 + 분산 처리를 동시에 도입하는 주기적 집계 쿼리 패턴]
- [Workflow-Driven Techniques for Large-Scale Traffic Processing - Kafka + Debezium을 활용한 CDC 패턴 설계]
- [Workflow-Driven Techniques for Large-Scale Traffic Processing - Temporal을 활용한 워크플로우 패턴]
- [Workflow-Driven Techniques for Large-Scale Traffic Processing - Docker와 경량 이미지를 활용한 환경 구축 방법]
- [Workflow-Driven Techniques for Large-Scale Traffic Processing - Kafka에서의 메시지 Delivery Guarantee]
- [Workflow-Driven Techniques for Large-Scale Traffic Processing - 실시간 동기화의 핵심 CDC]
- [Workflow-Driven Techniques for Large-Scale Traffic Processing - MySQL Binary Log 기반의 CDC]
- [Workflow-Driven Techniques for Large-Scale Traffic Processing - Binary Log 기반의 CDC 구현 플랫폼 Debezium이란?]
- [Workflow-Driven Techniques for Large-Scale Traffic Processing - Debezium Architecture]
- [Workflow-Driven Techniques for Large-Scale Traffic Processing - Debezium Architecture Best Practice와 주의사항]
- Reactive Programming - Everything About Reactive Programming & Core Concepts of Reactive Programming
- Reactive Programming - Mastering Reactor Operators in WebFlux & A Deep Dive into WebFlux Operators
- Reactive Programming - Best Practices and Optional Patterns in WebFlux & Key Practical Patterns for WebFlux Development
- Reactive Programming - Practical WebFlux Patterns with Spring Boot & Building Applications with Spring Boot and WebFlux
- ElasticSearch - Elasticsearch Core Concepts
- ElasticSearch - Basic Search Features & Architecture
- ElasticSearch - Korean Optimized Search
- ElasticSearch - Defining Data Types: Understanding Mappings
- ElasticSearch - Commonly Used Search APIs
- ElasticSearch - Managing Documents (1)
- ElasticSearch - Managing Documents (2)
- ElasticSearch - Analysis and Mapping
- ElasticSearch - All About Search
- ElasticSearch - Query Joins
- ElasticSearch - Processing Query Results
- ElasticSearch - Aggregations
- ElasticSearch - Tips for Improving Search Results
- [ElasticSearch - Elasticsearch Overview]
- [ElasticSearch - Elasticsearch Internals]
- [ElasticSearch - Monitoring]
- [ElasticSearch - Troubleshooting]
- Design Pattern - Strategy
- Design Pattern - Factory Method
- Design Pattern - Abstract Factory
- Design Pattern - Singleton
- Design Pattern - Observer
- Design Pattern - Publisher - Subscriber
- Design Pattern - Decorator
- Design Pattern - Adapter
- Design Pattern - Builder
- Design Pattern - Template Method
- Design Pattern - Facade
- Design Pattern - Proxy
- Design Pattern - Command
- Design Pattern - Chain of Responsibility
- Design Pattern - State
- Design Pattern - Composite
- [Clean Spring - Domain-Driven Development]
- [Clean Spring - Domain-Driven Development with Design Patterns]
- [Clean Spring - Developing Membership Application with Hexagonal Architecture]
- [Clean Spring - JPA and Domain Model Patterns]
- [Clean Spring - Designing a Consistent Domain Model with Aggregates]
- [Clean Spring - Web API Adapter]
- [Clean Spring - Hexagonal Architecture: Ports]
- [Clean Spring - Hexagonal Architecture: Application Components]
- [Clean Spring - Test Improvement & Architecture Validation]
- [Clean Spring - Developing Application Components]
- (Effective Java Item 1) Java ‐ 생성자 대신 정적 팩토리 메서드를 고려하라
- (Effective Java Item 2) Java - 생성자에 매개변수가 많다면 빌더를 고려하라
- (Effective Java Item 3) Java - private 생성자나 열거 타입으로 싱글턴임을 보증하라
- (Effective Java Item 4) Java - 인스턴스화를 막으려거든 private 생성자를 사용하라
- (Effective Java Item 5) Java - 자원을 직접 명시하지 말고 의존 객체 주입을 사용하라
- (Effective Java Item 6) Java ‐ 불필요한 객체 생성을 피하라
- (Effective Java Item 7) Java - 다 쓴 객체 참조를 해제하라
- (Effective Java Item 8) Java - finalizer와 cleaner 사용을 피하라
- (Effective Java Item 9) Java - try‐finally보다는 try‐with‐resources를 사용하라
- (Effective Java Item 10) Java ‐ equals는 일반 규약을 지켜 재정의하라
- (Effective Java Item 11) Java ‐ equals를 재정의하려거든 hashCode도 재정의하라
- (Effective Java Item 12) Java - toString을 항상 재정의하라
- (Effective Java Item 13) Java ‐ clone 재정의는 주의해서 진행하라
- (Effective Java Item 14) Java ‐ Comparable을 구현할지 고려하라
- (Effective Java Item 15) Java ‐ 클래스와 멤버의 접근 권한을 최소화하라
- (Effective Java Item 16) Java ‐ public 클래스에서는 public 필드가 아닌 접근자 메서드를 사용하라
- (Effective Java Item 17) Java ‐ 변경 가능성을 최소화하라
- (Effective Java Item 18) Java ‐ 상속보다는 컴포지션을 사용하라
- (Effective Java Item 19) Java ‐ 상속을 고려해 설계하고 문서화하라. 그러지 않았다면 상속을 금지하라
- (Effective Java Item 20) Java ‐ 추상 클래스보다는 인터페이스를 우선하라
- (Effective Java Item 21) Java ‐ 인터페이스는 구현하는 쪽을 생각해 설계하라
- (Effective Java Item 22) Java ‐ 인터페이스는 타입을 정의하는 용도로만 사용하라
- (Effective Java Item 23) Java ‐ 태그 달린 클래스보다는 클래스 계층구조를 활용하라
- (Effective Java Item 24) Java ‐ 멤버 클래스는 되도록 static으로 만들라
- (Effective Java Item 25) Java ‐ 톱레벨 클래스는 한 파일에 하나만 담으라
- (Effective Java Item 26) Java ‐ 로 타입은 사용하지 말라
- (Effective Java Item 27) Java ‐ 비검사 경고를 제거하라
- (Effective Java Item 28) Java ‐ 배열보다는 리스트를 사용하라
- (Effective Java Item 29) Java ‐ 이왕이면 제네릭 타입으로 만들라
- (Effective Java Item 30) Java ‐ 이왕이면 제네릭 메서드로 만들라
- (Effective Java Item 31) Java - 한정적 와일드카드를 사용해 API 유연성을 높이라
- (Effective Java Item 32) Java - 제네릭과 가변인수를 함께 쓸 때는 신중하라
- (Effective Java Item 33) Java ‐ 타입 안전 이종 컨테이너를 고려하라
- (Effective Java Item 34) Java - int 상수 대신 열거 타입을 사용하라
- (Effective Java Item 35) Java - ordinal 메서드 대신 인스턴스 필드를 사용하라
- (Effective Java Item 36) Java ‐ 비트 필드 대신 EnumSet을 사용하라
- (Effective Java Item 37) Java ‐ ordinal 인덱싱 대신 EnumMap을 사용하라
- (Effective Java Item 38) Java ‐ 확장할 수 있는 열거 타입이 필요하면 인터페이스를 사용하라
- (Effective Java Item 39) Java ‐ 명명 패턴보다 애너테이션을 사용하라[Effective Java Item 39]
- (Effective Java Item 40) Java ‐ @Override 어노테이션을 일괄되게 사용하라
- (Effective Java Item 41) Java ‐ 정의하려는 것이 타입이라면 마커 인터페이스를 사용하라
- (Effective Java Item 42) Java ‐ 익명 클래스보다는 람다를 사용하라
- (Effective Java Item 43) Java ‐ 람다보다는 메서드 참조를 사용하라
- (Effective Java Item 44) Java - 표준 함수형 인터페이스를 사용하라
- (Effective Java Item 45) Java - 스트림은 주의해서 사용하라
- (Effective Java Item 46) Java - 스트림에서는 부작용 없는 함수를 사용하라
- (Effective Java Item 47) Java - 반환 타입으로는 스트림보다 컬렉션이 낫다
- (Effective Java Item 48) Java ‐ 스트림 병렬화는 주의해서 사용하라
- (Effective Java Item 49) Java ‐ 매개변수가 유효한지 검사하라
- (Effective Java Item 50) Java ‐ 적시에 방어적 복사본을 만들라
- (Effective Java Item 51) Java ‐ 메서드 시그니처를 신중히 설계하라
- (Effective Java Item 52) Java ‐ 다중정의는 신중히 사용하라
- (Effective Java Item 53) Java ‐ 가변인수는 신중히 사용하라
- (Effective Java Item 54) Java - null이 아닌, 빈 컬렉션이나 배열을 반환하라
- (Effective Java Item 55) Java ‐ 옵셔널 반환은 신중히 하라
- (Effective Java Item 56) Java ‐ 공개된 API 요소에는 항상 문서화 주석을 사용하라
- (Effective Java Item 57) Java ‐ 지역변수의 범위를 최소화하라
- (Effective Java Item 58) Java ‐ 전통적인 for문보다는 for‐each문을 사용하라
- (Effective Java Item 59) Java ‐ 라이브러리를 익히고 사용하라
- (Effective Java Item 60) Java ‐ 정확한 답이 필요하다면 float와 double은 피하라
- (Effective Java Item 61) Java ‐ 박싱된 기본 타입보다는 기본 타입을 사용하라
- (Effective Java Item 62) Java ‐ 다른 타입이 적절하다면 문자열 사용을 피하라
- (Effective Java Item 63) Java ‐ 문자열 연결은 느리니 주의하라
- (Effective Java Item 64) Java ‐ 객체는 인터페이스를 사용해 참조하라
- (Effective Java Item 65) Java ‐ 리플렉션보다는 인터페이스를 사용하라
- (Effective Java Item 66) Java ‐ 네이티브 메서드는 신중히 사용하라
- (Effective Java Item 67) Java ‐ 최적화는 신중히 하라
- (Effective Java Item 68) Java ‐ 일반적으로 통용되는 명명 규칙을 따르라
- (Effective Java Item 69) Java ‐ 예외는 진짜 예외 상황에만 사용하라
- (Effective Java Item 70) Java ‐ 복구할 수 있는 상황에는 검사 예외를, 프로그래밍 오류에는 런타임 예외를 사용하라
- (Effective Java Item 71) Java ‐ 필요 없는 검사 예외 사용은 피하라
- (Effective Java Item 72) Java ‐ 표준 예외를 사용하라
- (Effective Java Item 73) Java ‐ 추상화 수준에 맞는 예외를 던지라
- (Effective Java Item 74) Java ‐ 메서드가 던지는 모든 예외를 문서화하라
- (Effective Java Item 75) Java ‐ 예외의 상세 메시지에 실패 관련 정보를 담으라
- (Effective Java Item 76) Java ‐ 가능한 한 실패 원자적으로 만들라
- (Effective Java Item 77) Java ‐ 예외를 무시하지 말라
- (Effective Java Item 78) Java - 공유 중인 가변 데이터는 동기화해 사용하라
- (Effective Java Item 79) Java - 과도한 동기화는 피하라
- (Effective Java Item 80) Java - 쓰레드보다는 실행자, 태스크, 스트림을 애용하라
- (Effective Java Item 81) Java - wait와 notify는 동시성 유틸리티를 애용하라
- (Effective Java Item 82) Java - 쓰레드 안전성 수준을 문서화하라
- (Effective Java Item 83) Java - 지연 초기화는 신중히 사용하라
- (Effective Java Item 84) Java - 프로그램의 동작을 쓰레드 스케줄러에 기대지 말라
- (Effective Java Item 85) Java - 자바 직렬화의 대안을 찾으라
- (Effective Java Item 86) Java - Serializable을 구현할지는 신중히 결정하라
- (Effective Java Item 87) Java - 커스텀 직렬화 형태를 고려해보라
- (Effective Java Item 88) Java - readObject 메서드는 방어적으로 작성하라
- (Effective Java Item 89) Java - 인스턴스 수를 통제해야 한다면 readResolve보다는 열거 타입을 사용하라
- [🔖(Effective Java Item 90) Java - 직렬화된 인스턴스 대신 직렬화 프록시 사용을 검토하라]
- (Effective Kotlin Item 1) Kotlin - 가변성을 제한하라
- (Effective Kotlin Item 2) Kotlin - 임계 영역을 제거하라
- (Effective Kotlin Item 3) Kotlin - 가능한 한 빨리 플랫폼 타입을 제거하라
- (Effective Kotlin Item 4) Kotlin - 변수의 스코프를 최소화하라
- (Effective Kotlin Item 5) Kotlin - 인수와 상태에 대한 기대치를 명시하라
- (Effective Kotlin Item 6) Kotlin - 사용자 정의 오류보다 표준 오류를 선호하라
- (Effective Kotlin Item 7) Kotlin - 결과가 없을 가능성이 있는 경우 널 가능 또는 Result 반환 타입을 선호하라
- (Effective Kotlin Item 8) Kotlin - use를 사용하여 리소스를 닫아라
- (Effective Kotlin Item 9) Kotlin - 단위 테스트를 작성하라
- (Effective Kotlin Item 10) Kotlin - 가독성을 목표로 설계하라
- (Effective Kotlin Item 11) Kotlin - 연산자의 의미는 함수의 이름과 일치해야 한다
- (Effective Kotlin Item 12) Kotlin - 가독성을 높이려면 연산자를 사용하라
- (Effective Kotlin Item 13) Kotlin - 타입 명시를 고려하라
- (Effective Kotlin Item 14) Kotlin - 리시버를 명시적으로 참조하라
- (Effective Kotlin Item 15) Kotlin - 프로퍼티는 동작이 아닌 상태를 나타내야 한다
- (Effective Kotlin Item 16) Kotlin - Unit?을 반환이나 연산에 사용하지 말라
- (Effective Kotlin Item 17) Kotlin - 이름 있는 인수 사용을 고려하라
- (Effective Kotlin Item 18) Kotlin - 코딩 컨벤션을 준수하라
- (Effective Kotlin Item 19) Kotlin - knowledge를 반복하지 말라
- (Effective Kotlin Item 20) Kotlin - 일반적인 알고리즘을 반복하지 말라
- (Effective Kotlin Item 21) Kotlin - 일반적인 알고리즘을 구현할 때 제네릭을 사용하라
- (Effective Kotlin Item 22) Kotlin - 타입 매개변수의 섀도잉을 피하라
- (Effective Kotlin Item 23) Kotlin - 제네릭 타입에 변성 한정자 사용을 고려하라
- (Effective Kotlin Item 24) Kotlin - 공통 모듈을 추출해서 여러 플랫폼에서 재사용하라
- (Effective Kotlin Item 25) Kotlin - 각각의 함수는 하나의 추상화 수준으로 작성하라
- (Effective Kotlin Item 26) Kotlin - 변경으로부터 코드를 보호하려면 추상화를 사용하라
- (Effective Kotlin Item 27) Kotlin - API 안정성을 명시하라
- (Effective Kotlin Item 28) Kotlin - 외부 API를 래핑하는 것을 고려하라
- (Effective Kotlin Item 29) Kotlin - 가시성을 최소화하라
- (Effective Kotlin Item 30) Kotlin - 문서로 규약을 정의하라
- (Effective Kotlin Item 31) Kotlin - 추상화 규약을 준수하라
- (Effective Kotlin Item 32) Kotlin - 보조 생성자 대신 팩토리 함수를 고려하라
- (Effective Kotlin Item 33) Kotlin - 이름 있는 선택적 인수를 갖는 기본 생성자 사용을 고려하라
- (Effective Kotlin Item 34) Kotlin - 복잡한 객체 생성을 위해 DSL 정의를 고려하라
- (Effective Kotlin Item 35) Kotlin - 의존성 주입을 고려하라
- (Effective Kotlin Item 36) Kotlin - 상속보다 합성을 선호하라
- (Effective Kotlin Item 37) Kotlin - 데이터 묶음을 표현할 때 data 한정자를 사용하라
- (Effective Kotlin Item 38) Kotlin - 연산과 행동을 전달하려면 함수 타입이나 함수형 인터페이스를 사용하라
- (Effective Kotlin Item 39) Kotlin - 제한된 계층구조를 표현하기 위해 sealed 클래스와 sealed 인터페이스를 사용하라
- [(Effective Kotlin Item 40) Kotlin - 태그 클래스 대신 클래스 계층구조를 선호하라]
- [(Effective Kotlin Item 41) Kotlin - 열거형 클래스를 사용해서 값 목록을 나타내라]
- [(Effective Kotlin Item 42) Kotlin - equals의 규약을 준수하라]
- [(Effective Kotlin Item 43) Kotlin - hashCode의 규약을 준수하라]
- [(Effective Kotlin Item 44) Kotlin - compareTo의 규약을 준수하라]
- [(Effective Kotlin Item 45) Kotlin - API의 필수적이지 않은 부분을 확장으로 추출하는 것을 고려하라]
- [(Effective Kotlin Item 46) Kotlin - 멤버 확장 함수를 피하라]
- (Effective Kotlin Item 3) Kotlin - variable
- (Effective Kotlin Item 4) Kotlin - primitive types, literals, and operations
- (Effective Kotlin Item 5) Kotlin - control Flow: if, when, try, and while
- (Effective Kotlin Item 6) Kotlin - function
- (Effective Kotlin Item 7) Kotlin - for
- (Effective Kotlin Item 8) Kotlin - Null Safety and Nullable Types
- (Effective Kotlin Item 9) Kotlin - Class
- (Effective Kotlin Item 10) Kotlin - Extend
- (Effective Kotlin Item 11) Kotlin - Data Class
- (Effective Kotlin Item 12) Kotlin - Object
- (Effective Kotlin Item 13) Kotlin ‐ Exception
- (Effective Kotlin Item 14) Kotlin - Enum Classes
- (Effective Kotlin Item 15) Kotlin - Sealed Classes and Interfaces
- (Effective Kotlin Item 16) Kotlin - Annotation Classes
- (Effective Kotlin Item 17) Kotlin - Extensions
- (Effective Kotlin Item 18) Kotlin - Collections
- (Effective Kotlin Item 19) Kotlin - Operator Overloading
- (Effective Kotlin Item 20) Kotlin - The Beauty of the Type System
- (Effective Kotlin Item 21) Kotlin - Generic
- Reactive Programming - Reactive Streams
- Reactive Programming - Blocking I/O & Non-Blocking I/O
- Reactive Programming - Reactor Outline
- Reactive Programming - Marble Diagram
- Reactive Programming - Cold Sequence & Hot Sequence
- [Reactive Programming - Backpressure]
- [Reactive Programming - Sinks]
- [Reactive Programming - Scheduler]
- [Reactive Programming - Context]
- [Reactive Programming - Debugging]
- [Reactive Programming - Testing]
- [Reactive Programming - Operators]
- [Reactive Programming - Spring Webflux]
- [Reactive Programming - Annotation Based Controller]
- [Reactive Programming - Functional Endpoint]
- [Reactive Programming - Spring Data R2DBC]
- [Reactive Programming - Exception Handling]
- [Reactive Programming - WebClient]
- [Reactive Programming - Reactive Streaming Data Processing]
- 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 사용자 수에 따른 규모 확장성
- 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 개략적인 규모 추정
- 가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 처리율 제한 장치의 설계
- [가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 안정 해시 설계]
- [가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 키-값 저장소 설계]
- [가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 분산 시스템을 위한 유일 ID 생성기 설계]
- [가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - URL 단축기 설계]
- [가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 웹 크롤러 설계]
- [가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 알림 시스템 설계]
- [가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 뉴스 피드 시스템 설계]
- [가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 채팅 시스템 설계]
- [가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 검색어 자동완성 시스템 설계]
- [가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 유튜브 설계]
- [가상 면접 사례로 배우는 대규모 시스템 설계 기초 1 - 구글 드라이브 설계]
- (Clean Code 2) Clean Code - 의미 있는 이름
- (Clean Code 3) Clean Code - 함수
- (Clean Code 4) Clean Code - 주석
- (Clean Code 5) Clean Code - 형식 맞추기
- (Clean Code 6) Clean Code - 객체와 자료구조
- (Clean Code 7) Clean Code - 오류 처리
- (Clean Code 8) Clean Code - 경계
- (Clean Code 9) Clean Code - 단위 테스트
- (Clean Code 10) Clean Code - 클래스
📖 리팩토링 2판⭐
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 - 느려진 서비스, 어디부터 봐야 할까
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 - 성능을 좌우하는 DB 설계와 쿼리
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 ‐ 외부 연동이 문제일 때 살펴봐야 할 것들
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 ‐ 비동기 연동, 언제 어떻게 써야 할까
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 ‐ 동시성, 데이터가 꼬이기 전에 잡아야 한다
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 ‐ IO 병목, 어떻게 해결하지
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 ‐ 실무에서 꼭 필요한 보안 지식
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 ‐ 모르면 답답해지는 네트워크 기초
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 ‐ 자주 쓰는 서버 구조와 설계 패턴
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 ‐ 처음 해보는 성능 테스트를 위한 기본 정리
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 ‐ NoSQL 이해하기
- 주니어 백엔드 개발자가 반드시 알아야 할 실무 지식 ‐ DB로 분산 잠금 구현하기
- 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴 - 객체 지향
- 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴 - 다형성과 추상 타입
- 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴 - 재사용: 상속보단 조립
- 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴 - 설계 원칙: SOLID
- 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴 - DI(Dependency Injection)와 서비스 로케이터
- 개발자가 반드시 정복해야 할 객체 지향과 디자인 패턴 - 주요 디자인 패턴
- [Real MySQL 8.0 - 인덱스]
- [Real MySQL 8.0 - 실행 계획]
- [Real MySQL 8.0 - 아키텍처]
- [Real MySQL 8.0 - 트랜잭션과 잠금]
- 객체지향의 사실과 오해(역할, 책임, 협력 관점에서 본 객체지향) - 협력하는 객체들의 공동체
- 객체지향의 사실과 오해(역할, 책임, 협력 관점에서 본 객체지향) - 이상한 나라의 객체
- [객체지향의 사실과 오해(역할, 책임, 협력 관점에서 본 객체지향) - 타입과 추상화]
- [객체지향의 사실과 오해(역할, 책임, 협력 관점에서 본 객체지향) - 역할, 책임, 협력]
- [객체지향의 사실과 오해(역할, 책임, 협력 관점에서 본 객체지향) - 책임과 메시지]
- [객체지향의 사실과 오해(역할, 책임, 협력 관점에서 본 객체지향) - 객체 지도]