일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | |||
5 | 6 | 7 | 8 | 9 | 10 | 11 |
12 | 13 | 14 | 15 | 16 | 17 | 18 |
19 | 20 | 21 | 22 | 23 | 24 | 25 |
26 | 27 | 28 | 29 | 30 | 31 |
- react
- JSBridge
- ASP.NET
- CSS
- 캐나다취준
- JUNCTION2023
- 이펙티브타입스크립트
- framer
- 글또 10기
- 글또
- Semantic Versioning
- 알고리즘
- SemVer
- useState
- 회고
- React-Router-Dom
- CSS방법론
- 캐나다개발자
- typescript
- 테오의 스프린트
- VS Code
- Framer motion
- TS
- 타입스크립트
- 코드트리
- 개발자 원칙
- 시스템디자인
- Effective Typescript
- 개발자를 위한 글쓰기 가이드
- framer-motion
- Today
- Total
큰 꿈은 파편이 크다!!⚡️
시스템 디자인 인터뷰 준비 (1) - 개념 정리 본문
어느 정도 연차가 쌓여서 피할 수 없는 시스템 디자인 인터뷰를 공부해보다가, 실전 예제와 함께 정리해보려 한다.
이 시리즈는 이렇게 계획했다.
1) 시스템 디자인 인터뷰에 필요한 개념들을 확실히 함 <- NOW!
2) 디자인 패턴 (LLD 할때 자주 쓰임)
3) 시스템 디자인 시 각 컴포넌트의 역할 (HLD)
4) 예제 및 각 컴포넌트에 적용한 내용 뜯어보기
1. High-Level System Design (HLD) vs Low-Level System Design (LLD)
1) High-Level System Design (HLD)
시스템이 "무엇”을 해야 하는지 정의하고 주요 구성 요소와 이들 간의 상호작용을 설계한다. 설계 내용에는 전체 시스템 아키텍처, 데이터 흐름, 외부 인터페이스 등이 포함된다.
설계 내용 ex:
- 데이터베이스 유형 결정 (SQL vs NoSQL)
- API 게이트웨이 구조 설계
- 주요 모듈 분할 및 상호작용 정의
면접질문 예시:
- 차량 공유 서비스를 설계하라
- 음식 배달 서비스를 설계하라
2) Low-Level System Design (LLD)
시스템이 "어떻게" 동작할지를 정의하고 시스템 내부 구성 요소의 세부적인 동작을 설계한다. 클래스 다이어그램, 메서드 설계 등의 세부 로직이 포함되며, HLD의 세부내용을 상세화한다.
설계 내용 ex:
- 데이터베이스 테이블 설계 및 관계 정의
- API 엔드포인트 세부 구현
면접질문 예시:
- 자판기를 설계하라
- 엘리베이터를 설계하라
2. SOLID 디자인 원칙
SOLID 원칙은 객체 지향 설계에서 높은 응집도와 낮은 결합도를 유지하기 위한 설계 가이드라인이다. 원칙들을 유념하면서 디자인하고, 설계 후 이 원칙을 만족하는지 검증해본다.
1) S: 단일 책임 원칙 (Single Responsibility Principle)
하나의 클래스는 하나의 책임을 가진다.
ex. 사용자 관리 클래스는 사용자 인증과 데이터 저장을 동시에 처리하지 않고, 각각 인증 클래스와 저장 클래스를 만들어 역할을 분리한다
2) O: 개방-폐쇄 원칙 (Open-Closed Principle)
클래스는 기존 코드를 수정하지 않고도 시스템을 확장할 수 있도록 설계한다.
ex. 새로운 기능 추가 시 기존 클래스를 수정하지 않고, 상속이나 인터페이스를 통해 구현한다
적용 시나리오: 보고서를 생성하는 시스템이 있다. 처음에는 PDF 보고서만 생성하다가, 이후 Excel 보고서를 추가해야 하는 상황이다.
OCP를 위반한 설계
- 새로운 포맷(예: Word 보고서)을 추가할 때마다 generate 메서드를 수정해야 함
- 기존 코드 수정은 새로운 버그를 유발할 가능성이 있음
class ReportGenerator {
generate(format) {
if (format === "PDF") {
console.log("Generating PDF report...");
} else if (format === "Excel") {
console.log("Generating Excel report...");
}
}
}
// 사용 예
const reportGenerator = new ReportGenerator();
reportGenerator.generate("PDF"); // Output: Generating PDF report...
reportGenerator.generate("Excel"); // Output: Generating Excel report...
OCP를 준수한 설계
- 새로운 포맷이 추가될 때 기존 코드를 수정하지 않아도 됨
- 기존 generate_report 함수와 클래스들은 수정 없이 재사용 가능
- 추상화를 사용하여 새로운 기능을 추가할 때 기존 코드를 수정하지 않고 확장할 수 있도록 설계
// 추상화: Report 클래스
class Report {
generate() {
throw new Error("Method 'generate()' must be implemented.");
}
}
// PDF 보고서 클래스
class PDFReport extends Report {
generate() {
console.log("Generating PDF report...");
}
}
// Excel 보고서 클래스
class ExcelReport extends Report {
generate() {
console.log("Generating Excel report...");
}
}
// 클라이언트 코드
class ReportGenerator {
generateReport(reportInstance) {
if (!(reportInstance instanceof Report)) {
throw new Error("Invalid report type. Must implement the Report interface.");
}
reportInstance.generate();
}
}
// 사용 예
const reportGenerator = new ReportGenerator();
const pdfReport = new PDFReport();
const excelReport = new ExcelReport();
reportGenerator.generateReport(pdfReport); // Output: Generating PDF report...
reportGenerator.generateReport(excelReport); // Output: Generating Excel report...
3) L: 리스코프 치환 원칙 (Liskov Substitution Principle)
상위 클래스의 객체를 하위 클래스로 교체해도 프로그램이 정상적으로 작동해야 한다.
ex. Rectangle 클래스를 상속한 Square 클래스가 상위 클래스의 기능을 모두 제대로 수행해야 한다
4) I: 인터페이스 분리 원칙 (Interface Segregation Principle)
인터페이스에 불필요한 메서드를 포함하지 않는다.
ex. 동물 인터페이스에서 모든 동물이 날지 않으므로, Flyable과 Walkable 인터페이스를 별도로 나눈다
// 공통 Animal 인터페이스
class Animal {
eat() {
console.log("This animal is eating.");
}
}
// Flyable 인터페이스
class Flyable {
fly() {
console.log("This animal is flying.");
}
}
// Walkable 인터페이스
class Walkable {
walk() {
console.log("This animal is walking.");
}
}
// Bird 클래스: Animal + Flyable
class Bird extends Animal {
constructor() {
super();
this.flyable = new Flyable();
}
fly() {
this.flyable.fly();
}
}
// Dog 클래스: Animal + Walkable
class Dog extends Animal {
constructor() {
super();
this.walkable = new Walkable();
}
walk() {
this.walkable.walk();
}
}
5) D: 의존 역전 원칙 (Dependency Inversion Principle)
고수준 모듈(High-Level Module)과 저수준 모듈(Low-Level Module) 모두 추상화(ex.인터페이스)에 의존한다.
ex. 데이터베이스를 사용하는 코드가 특정 데이터베이스 구현체가 아니라 인터페이스에 의존하도록 설계한다
적용 시나리오: 어떤 주문내역을 db에 저장해야 하는 상황
DIP를 위반한 설계
- 고수준 모듈(OrderService)이 특정 저수준 모듈(SQLDatabase)에 강하게 결합되어 있음.
- 데이터 저장 방식을 변경하려면 OrderService를 수정해야 함.
- 테스트 시 다른 데이터베이스를 사용하는 경우 교체가 어렵고 유연성이 낮음.
// 저수준 모듈
class SQLDatabase {
save(data) {
console.log("Saving data in SQL database...");
}
}
// 고수준 모듈
class OrderService {
constructor() {
this.database = new SQLDatabase(); // 특정 구현체에 의존
}
saveOrder(order) {
this.database.save(order);
}
}
// 사용 예
const orderService = new OrderService();
orderService.saveOrder("Order 1");
DIP를 준수한 설계
- 고수준 모듈은 추상화(인터페이스)에 의존하고, 저수준 모듈이 해당 인터페이스를 구현하도록 설계.
// 추상화(인터페이스 역할)
class Database {
save(data) {
throw new Error("Method 'save()' must be implemented.");
}
}
// 저수준 모듈 - SQL Database
class SQLDatabase extends Database {
save(data) {
console.log("Saving data in SQL database...");
}
}
// 저수준 모듈 - NoSQL Database
class NoSQLDatabase extends Database {
save(data) {
console.log("Saving data in NoSQL database...");
}
}
// 고수준 모듈
class OrderService {
constructor(database) {
this.database = database; // 추상화에 의존
}
saveOrder(order) {
this.database.save(order);
}
}
// 사용 예
const sqlDatabase = new SQLDatabase();
const nosqlDatabase = new NoSQLDatabase();
// SQL 데이터베이스 사용
const orderServiceSQL = new OrderService(sqlDatabase);
orderServiceSQL.saveOrder("Order 1"); // Output: Saving data in SQL database...
// NoSQL 데이터베이스 사용
const orderServiceNoSQL = new OrderService(nosqlDatabase);
orderServiceNoSQL.saveOrder("Order 2"); // Output: Saving data in NoSQL database...
'기타 CS' 카테고리의 다른 글
[시스템 디자인 학습] Load balancer (1) | 2024.11.10 |
---|---|
ASP.NET으로 서버&리액트 프로젝트 서빙하기 (0) | 2024.02.04 |
Microsoft.IdentityModel 인증 + 리액트 (0) | 2023.07.30 |
“런타임”이라는 단어를 이제는 사용할거야 (0) | 2023.07.02 |
내가 정착한 VSCode 환경 설정 💞 소개 (0) | 2023.05.20 |