-
Notifications
You must be signed in to change notification settings - Fork 0
Readable Code
woo jin edited this page Dec 22, 2025
·
14 revisions
- 가독성(Readability) = 글이 잘 읽힌다 = 이해가 잘 된다 = 유지보수 하기 수월하다 = 시간과 자원이 절약된다.
- 추상 = 특정 측면을 가려내고 불필요한 부분을 버린다.
-
✅️이름을 짓는다는 행위는 추상적 사고를 기반으로 한다.
- 표현하고자 하는 구체에서 정말 중요한 핵심 개념만을 추출해 잘 드러내는 표현
- 우리 도메인의 문맥 안에서 이해되는 용어
-
✅️단수와 복수를 구분하자.
- 끝에 -(e)s를 붙여 어떤 데이터가 단수인지 복수인지를 나타내는 것만으로도 읽는 이에게 중요한 정보를 같이 전달할 수 있다.
-
✅️이름을 줄이지 않는다.
- 가독성을 희생해 얻는 부분으로 잃는 것에 비해 얻는 것이 적다.
-
✅️은어/방언 사용하지 않는다.
- 도메인 용어를 먼저 정의하는 과정이 먼저 필요할 수도 있다. 모두가 아는 것으로 사용하는 것이 좋다.
-
✅️한 메서드의 주제는 반드시 하나다.
- 메서드의 이름으로 구체적인 내용을 추상화한다.
- ✅️생략할 정보와 의미를 부여하고 드러낼 정보를 구분한다.
- ✅️void 대신 반환할 만한 값이 있는지 고민해본다. -> 반환값이 있다면 테스트가 용이하기 때문에
-
✅️매직 넘버/매직 스트링 : 의미를 갖고 있으나 상수로 추출되지 않은 숫자, 문자열 등
- 이런 상수 추출을 통해 이름을 짓고 의미를 부여함으로써 기독성과 유지보수성을 높인다.
- Early Return으로 else의 사용을 지양하자.
// bad❌
int get_string_type(char* string) {
int ret = 0;
if (is_rule_1(string)) {
ret = 1;
} else {
if (is_rule_2(string)) {
ret = 2;
} else {
ret = 0;
}
}
return ret;
}
// good⭕
int is_valid(char* string) {
if (is_rule_1(string))
return 1;
if (is_rule_2(string))
return 2;
return 0;
}- 복잡한 로직의 끊어 읽기가 필요한 경우
// bad❌ -> 2번의 사고를 필요로 한다.
if (!isLeftDirection()) {
doSomething();
}
// good⭕ -> 1번이면 끝
if (isNotLeftDirection()) {
doSomething();
}- 부정어구를 쓰지 않아도 되는 상황인가를 체크한다.
- 부정어 의미를 담고 있는 다른 단어가 존재하는지 고민하고 직접 부정어구로 메서드명을 구성한다.
- 부정 연산자(!)의 가독성 저해
- 예외가 발생할 가능성을 낮추는 것에 초점을 맞춰야 한다.
- 의도한 예외와 의도치않은 예외를 구분한다.
- 사용자에게 보여줄 예외와 개발자가 보고 처리해야 할 예외를 구분한다.
- NPE를 방지하는 방향으로 경각심을 가져야 한다.
- 메서드 설계 시 return null을 자제한다. 만약 어렵다면 Optional 사용을 고려해본다.
- Optional
- Optional은 비싼 객체이다. 꼭 필요한 상황에서 반환 타입에 사용한다.
- Optional은 절대로 파라미터로 받지 않도록 한다. 분기 케이스를 따져야하기 때문이다.
- Optional을 반환받았다면 빠르게 해소한다.
- orElse(), orElseGet(), orElseThrow() 차이를 숙지하고 사용하도록 한다.
-
Optional이 비어있을 경우, 지정된 예외(Exception)를 발생시킨다.
-
Optional이 비어있을 경우에만, 파라미터로 받은 Supplier (람다식 또는 메서드 레퍼런스)를 실행하여 그 결과값을 반환한다.- 대체할 기본값이 새로운 객체 생성을 필요로 할 때
- 대체할 값을 가져오기 위해 다른 메서드를 호출하거나 복잡한 연산이 필요할 때
- Optional이 비어있을 경우, 파라미터로 받은 값을 그대로 반환한다.
- 불필요한 연산 : orElse() 안에 객체 생성이나 메서드 호출 코드를 넣으면, Optional에 값이 존재해서 그 결과가 필요 없음에도 불구하고 무조건 실행되어 성능 저하의 원인이 될 수 있다.
- 객체(Object) = 데이터 + 코드
- 객체 추상화 필요 단계
- 비공개 필드(데이터) + 비공개 로직(코드)
- 공개 메서드 선언부를 통해 외부 세계와 소통
- 객체 책임이 나뉨에 따라 객체 간 협력이 발생
- ✅️1개의 관심사로 명확하게 책임이 정의되었는가?
-
✅️생성자 혹은 정적 팩토리 메서드에서 유효성 검증이 가능하다.
- 도메인에 특화된 검증 로직이 들어갈 수 있다.
class Money {
private long value;
public Money(long value) {
if (value < 0) {
throw new IllegalArgumentException("돈은 0원 이상이어야 합니다.");
}
this.value = value;
}
}-
✅️setter 사용은 자제한다.
- 데이터는 불변이 최고다. 변하는 데이터라도 객체가 핸들링할 수 있어야 한다.
- 객체 내부에서 외부 세계의 개입 없이 자체적인 변경/가공으로 처리할 수 있는가를 확인한다.
- 만약 외부에서 가지고 있는 데이터로 데이터 변경 요청을 하는 경우
set~보다는update~와 같이 의도를 드러내는 네이밍을 고려한다.
-
✅️getter도 처음엔 사용을 자제한다. 필요한 경우에만 추가하도록 한다.
- 외부에서 객체 내의 데이터가 필요하다고 getter를 남발하는 것은 무례한 행동이다. 객체에 메시지를 요청한다.
Person person = new Person();
// bad❌
if (person.getWallet().getID().findAge() >= 19) {
pass();
}
// good⭕
if (person.isAgeGraterThanOrEqualTo(19) {
pass();
}-
✅️객체의 필드 수를 줄여라.
- 불필요한 데이터가 많을수록 복잡도가 높아지고 대응할 변화가 많아진다.
- 메서드 기능으로도 제공이 가능하면 OK, 단 미리 가공하는 것이 성능 상의 이점이 있다면 필드로 가지고 있는 것도 좋을 순 있다.
class Bill {
private final List<Menu> menus;
private final long totalPrice; // 미리 가지고 있을수도 있는 값
// 메서드 기능으로 충분히 계산할 수 있는 값
public long calculateTotalPrice() {
return this.menus.stream()
.mapToLong(Menu::getPrice)
.sum();
}
}-
✅️하나의 클래스는 단 한 가지 변경 이유만을 가져야 한다.
- 객체가 가진 공개 메서드, 필드, 상수 등은 해당 객체의 단일 책임에 의해서만 변경이 되어야 한다.
-
✅️확장에는 열려 있고, 수정에는 닫혀 있어야 한다.
- 기존 코드 변경없이, 시스템 기능을 확장할 수 있어야 한다.
- 추상화와 다형성을 활용해 OCP를 지킬 수 있다. 구체에 의존하기보다 인터페이스로 분리해야 한다.
- 무엇이, 왜 변할 것인가?
- 변하는 것을 '약속(인터페이스)'으로 만들었는가?
- '역할'과 '책임'을 명확히 분리했는가?
- '상속' 대신 '구성'을 사용하고 있는가?
-
✅️상속 구조에서 부모 클래스의 인스턴스를 자식 클래스의 인스턴스로 치환할 수 있어야 한다.
- 자식 클래스는 부모 클래스의 책임을 준수하며, 부모 클래스의 행동을 변경하지 않아야 한다.
- LSP를 위반하면 상속 클래스를 사용할 때 오동작, 예상 박의 예외가 발생하거나 이를 방지하기 위한 불필요한 타입 체크가 동반될 수 있다.
-
✅️클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안된다. 그러니 인터페이스를 잘게 쪼개라.
- ISP를 위반하면 불필요한 의존성으로 인해 결합도가 높아지고 특정 기능 변경이 여러 클래스에 영향을 미칠 수 있다.
- ✅️상위 수준 모듈은 하위 수준 모듈에 의존해서는 안 된다. 둘 모두 추상화에 의존해야 한다.
- 상속보다는 조합을 사용하자.
- 상속은 시멘트처럼 굳어지는 구조다. 수정이 어렵다.
- 조합과 인터페이스를 활용하는 것이 유연한 구조를 만든다.
- 기본 타입을 객체로 감싸고 의미를 부여해 추상화
- 도메인의 어떤 개념을 추상화하여 표현한 값 객체
- 값으로 취급하기 위해서 불변성, 동등성, 유효성 검증 등을 보장해야 한다.
- 불변성 : final 필드, setter 금지
- 동등성 : 서로 다른 인스턴스여도 내부 값이 같으면 같은 값 객체로 취급한다. 이 때,
equals()와hashCode()재정의가 필요하다. - 유효성 검증 : 객체가 생성되는 시점에 대해 유효성을 검증
public class Money {
private final long value;
public Money(long value) {
if (value < 0) {
throw new IllegalArgumentException("돈은 0원 이상이어야 합니다.");
}
this.value = value;
}
// equals() & hashCode() 재정의
}
Money money1 = new Money(10000);
Money money2 = new Money(10000); // 같은 10000원이라는 표면적 가치를 지니기 때문에 같은 돈이다.- Entity는 식별자가 존재한다. 식별자가 아닌 필드의 값이 달라도 식별자가 같으면 동등한 객체로 취급한다.
- equals() & hashCode()도 식별자 필드만 가지고 재정의할 수 있다.
- 식별자가 같은데 식별자가 아닌 필드 값이 서로 다른 두 인스턴스가 있다면 시간이 지남에 따라 변화한 것으로 볼 수 있다.
- VO는 식별자 없이 내부 모든 값이 다 같아야 동등한 객체로 취급한다.
// bad❌
public class Lotto {
private final List<Integer> numbers; // 로또 번호를 List로 직접 관리
public Lotto(List<Integer> numbers) {
// 유효성 검증 로직이 외부에 노출되거나 중복될 가능성이 높음
if (numbers.size() != 6) {
throw new IllegalArgumentException("로또 번호는 6개여야 합니다.");
}
this.numbers = numbers;
}
// numbers를 직접 반환하여 외부에서 수정될 위험이 있음 (getNumbers().add(7))
public List<Integer> getNumbers() {
return numbers;
}
public boolean contains(int number) {
// 컬렉션에 대한 단순 위임 메서드
return numbers.contains(number);
}
}
// good⭕
public class LottoNumbers {
private static final int LOTTO_NUMBER_SIZE = 6;
private final List<Integer> numbers;
public LottoNumbers(List<Integer> numbers) {
validateSize(numbers); // 생성 시점에 유효성 검증
validateDuplicate(numbers); // 중복 검증
this.numbers = numbers;
}
// 비즈니스 로직을 내부에 포함 (예: 특정 숫자를 포함하는지)
public boolean contains(int number) {
return this.numbers.contains(number);
}
// 당첨 번호와 몇 개가 일치하는지 계산하는 비즈니스 로직
public int countMatch(LottoNumbers winningNumbers) {
return (int) this.numbers.stream()
.filter(winningNumbers::contains)
.count();
}
// 외부에서 컬렉션을 직접 수정하지 못하도록 방어적 복사 또는 unmodifiableList 반환
public List<Integer> getNumbers() {
return Collections.unmodifiableList(numbers);
}
private void validateSize(List<Integer> numbers) {
if (numbers.size() != LOTTO_NUMBER_SIZE) {
throw new IllegalArgumentException("로또 번호는 " + LOTTO_NUMBER_SIZE + "개여야 합니다.");
}
}
private void validateDuplicate(List<Integer> numbers) {
if (numbers.stream().distinct().count() != numbers.size()) {
throw new IllegalArgumentException("로또 번호는 중복될 수 없습니다.");
}
}
}
// Lotto 클래스는 이제 LottoNumbers 일급 컬렉션을 사용
public class Lotto {
private final LottoNumbers lottoNumbers;
private final int bonusNumber;
public Lotto(LottoNumbers lottoNumbers, int bonusNumber) {
this.lottoNumbers = lottoNumbers;
this.bonusNumber = bonusNumber;
}
// ... 당첨 여부 확인 등 Lotto와 관련된 다른 로직 ...
}- Enum은 상수의 집합이며 상수와 관련된 로직을 담을 수 있는 공간이다.
- 특정 도메인 개념에 대해 그 종류와 기능을 명시적으로 표현해줄 수 있다.
- 변경이 정말 잦은 개념은 Enum보다 DB로 관리하는 것이 나을 수 있다.
사실 주석에 대한 부분은 여태까지 개발을 하면서 크게 신경을 쓰지 않았다. 오히려 나는 주석이라는 것이 이러이러한 것임을 설명하기에 좋다고 생각했었다.
- 주석을 작성할 때 자주 변하는 정보는 최대한 지양해서 작성한다.
- 만약 관련 정책이 변하거나 코드가 변경되었다면 주석도 잊지 않고 함께 업데이트한다.
- 좋은 주석이란, 모든 표현 방법을 총동원해 코드에 의도를 녹여내고 그럼에도 불구하고 전달해야 할 정보가 남았을 때 사용하는 것이 좋은 주석이 된다.
개발하면서 파라미터의 순서나 변수, 메서드 등의 순서도 크게 신경쓰지 않았던 것 같다.
- 변수는 사용하는 순서대로 나열한다.
- 메서드의 순서도 고려해보아야 하는데, 객체 입장에서 생각해본다.
- 필요한 적정 수준보다 더 높은 수준의 엔지니어링
- 구현체가 하나인 인터페이스
- 코드 탐색에 영향을 줄 수 있다.
- 너무 이른 추상화
- 정보가 숨겨지기 때문에 오히려 복잡도가 높아진다.
- 후대 개발자들이 선대의 의도를 파악하기 어렵다.
- 지속 가능한 소프트웨어의 품질이냐 기술 부채를 안고 가는 빠른 결과물이냐
- 클린 코드를 추구하지 말라는 것이 아니라 미래 시점에서도 잘 고칠 수 있도록 하는 코드 센스가 필요
- 패키지는 문맥으로써의 정보를 제공할 수 있는 중요한 요소다.
- 패키지를 쪼개지 않으면 관리가 어려워진다.
- 그렇다고 해서 패키지를 너무 잘게 쪼개면 마찬가지로 관리가 어려워진다.
-
✅️대규모 패키지 변경은 팀원과의 합의를 이룬 시점에 하자.
- 현재 기준으로 본인만 변경하고 있는 부분이라면 괜찮으나 여러 사람이 변경 중인 부분이나 공통으로 사용하는 클래스 패키지를 한 번에 변경하면 추후 Conflict가 발생할 수 있다.
- 처음 만들 때부터 잘 고민해서 패키지를 나눠놓는 것이 제일 좋다.
- DTO(Data Transfer Object)는 데이터를 전달하기 위한 객체이다. 계층 간 데이터를 주고받을 때, 데이터를 담아서 전달한다.
- 여러 레이어 사이에서 DTO를 사용할 수 있다.
- DTO는 Getter/Setter 메서드를 포함한다. 이외의 비즈니스 로직은 포함하지 않는다.
- VO는 값 자체를 표현하는 객체이다.
- VO는 객체들의 주소가 달라도 값이 같으면 동일한 것으로 간주한다.
- 고유번호가 서로 다른 10,000원 지폐 2장이 있다고 가정할 때, 이 두 지폐의 고유번호는 다르지만 액면가가 같기 때문에 동일하다고 말할 수 있다.
- VO는 Getter 메서드와 함께 비즈니스 로직도 포함할 수 있다.
- 단, Setter 메서드는 가지지 않는다.(불변성을 보장해야하기 때문에)
- 또, 값 비교를 위해
equals(),hashCode()메서드를 오버라이딩 해줘야 한다. - Java의 record 타입은 자바 16에 도입된 특별한 유형의 클래스로, 불변 데이터를 간편하게 저장하기 위해 설계되었다.
- 컴파일러가 메서드를 구성 요소 기반으로 자동으로 구현해주기 때문에 직접 구현할 필요가 없이 바로 사용 가능하다.
- Entity는 실제 DB 테이블과 매핑되는 핵심 클래스이다.
- 이를 기준으로 테이블이 생성되고 스키마가 변경된다.
- 따라서, 절대로 Entity를 요청이나 응답값을 전달하는 클래스로 사용해선 안 된다.
- Entity는 기본적으로 PK(Primary Key) 즉, ID로 식별이 되고 비즈니스 로직을 포함할 수 있다.
- 코드 포맷 정렬 : Option + Cmd + L
- SonarQube : 잠재적인 문제가 될 수 있는 오류, 버그, 스타일 등을 미리 알려주는 코드 품질 체크 도구
- 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⭐
- 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 - 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]
- Reactive Programming - Reactive System & Reactive Programming
- Reactive Programming - Fundamentals of WebFlux and Reactor
- Reactive Programming - Core Operators in WebFlux Reactor
- Reactive Programming - Practical Patterns in WebFlux
- Reactive Programming - WebFlux Patterns with Spring Boot
- 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
- MySQL ‐ Solving Concurrency Problems using Database-Level Locking
- MySQL - Checking DB Metrics with SQL Queries
- MySQL - Data Modeling for Practical Service Development
- MySQL - Basic CRUD in MySQL
- MySQL - MySQL Horizontal Scaling
- 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
- MySQL - Multi Column Index
- MySQL - Covering Index
- MySQL - ORDER BY
- MySQL - INSERT
- MySQL - AUTO_INCREMENT_LOCK
- MySQL - MySQL LockType
- MySQL - DeadLock Case
- MySQL - NoOffset For Query Tuning
- 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 pub/sub & streams
- 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 - DeComposition Patterns
- MicroService Architecture - Service Communications Patterns
- 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 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
- 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
- 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 - Advanced Concept]
- [Nginx - Advanced Reverse Proxy]
- [Monitoring - Log Concept]
- [Monitoring - Log Level & Filter]
- [Monitoring - Logback]
- [Monitoring - Log Collection with ELK Stack]
- [Monitoring - Log Monitoring with Kibana]
- [Monitoring - Building a Monitoring System with Spring Boot Actuator]
- [Monitoring - Server Monitoring with Prometheus and Grafana with Discord Alerts]
- Test - Load Testing Fundamentals
- [Test - Diagnosing Bottlenecks via Load Testing]
- [Test - Performance Tuning: Resolving Bottlenecks]
- Test - JUnit5
- Test - Mockito
- Test - TestContainers
- Test - JMeter
- Test - Chaos Monkey
- [Test - ArchUnit]
- [Test - Unit Testing Essentials]
- [Test - TDD]
- [Test - Testing with Spring & JPA]
- Test - A Guide to Effective Mocking
- Test - Appendix: Tips for Better Testing
- (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]
- (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 - 클래스⭐