객체지향 프로그래밍(OOP)
객체지향 프로그래밍(Object-Oriented Programming, 이하 OOP)은 현실 세계를 프로그래밍에 그대로 옮겨놓은 것처럼 객체 단위로 코드를 구성하는 프로그래밍 패러다임입니다.
OOP에서는 데이터와 그 데이터를 다루는 함수를 하나의 단위로 묶어서 객체(Object) 라고 부릅니다.
이 객체들 간의 상호작용을 통해 프로그램이 동작하게 됩니다.
객체지향의 4대 특성
1. 캡슐화(Encapsulation)
- 객체 내부의 상태(data)를 외부에서 직접 접근하지 못하도록 감추고, 메서드를 통해서만 접근하게 하는 원칙입니다.
- 예: private 프로퍼티, public getter/setter
class User {
private password: string;
constructor(password: string) {
this.password = password;
}
checkPassword(input: string): boolean {
return this.password === input;
}
}
2. 상속(Inheritance)
- 부모 클래스의 속성과 메서드를 자식 클래스가 물려받을 수 있게 합니다.
- 코드 재사용성과 확장성에 큰 장점이 있음
class Animal {
move() {
console.log("움직이는 중");
}
}
class Dog extends Animal {
bark() {
console.log("멍멍!");
}
}
3. 다형성(Polymorphism)
- 같은 메서드 이름이라도 상황에 따라 다른 동작을 하도록 만드는 것입니다.
- 오버라이딩, 오버로딩이 이에 해당
class Animal {
speak() {
console.log("소리를 낸다");
}
}
class Cat extends Animal {
speak() {
console.log("야옹");
}
}
4. 추상화(Abstraction)
- 불필요한 내부 구현은 숨기고, 꼭 필요한 기능만 외부에 노출
- 인터페이스나 추상 클래스로 구현
abstract class Shape {
abstract getArea(): number;
}
class Rectangle extends Shape {
constructor(private width: number, private height: number) {
super();
}
getArea(): number {
return this.width * this.height;
}
}
예시: 게시글 필터링 기능
어떤 게시판 페이지에서 아래와 같은 기능이 있다고 가정해봅시다.
- 게시글 리스트를 날짜, 조회수, 좋아요 순 등으로 정렬할 수 있음
- 필터 타입은 점점 늘어날 수 있음
- 확장성 있는 방식으로 필터 로직을 구성하고 싶음
나쁜 예시: if문 덕지덕지
function sortPosts(posts: Post[], sortType: string): Post[] {
if (sortType === "latest") {
return [...posts].sort((a, b) => b.createdAt - a.createdAt);
}
if (sortType === "views") {
return [...posts].sort((a, b) => b.viewCount - a.viewCount);
}
if (sortType === "likes") {
return [...posts].sort((a, b) => b.likeCount - a.likeCount);
}
return posts;
}
- 처음에는 간단해 보여도 필터 종류가 늘어날수록 조건문이 꼬리를 물고 늘어지고, 테스트도 어려워집니다.
좋은 예시: 전략 패턴 + 객체지향
// 1. 필터 전략을 추상화 (추상 클래스 or 인터페이스)
interface PostSortStrategy {
sort(posts: Post[]): Post[];
}
// 2. 각각의 전략 구현
class LatestSort implements PostSortStrategy {
sort(posts: Post[]): Post[] {
return [...posts].sort((a, b) => b.createdAt - a.createdAt);
}
}
class ViewSort implements PostSortStrategy {
sort(posts: Post[]): Post[] {
return [...posts].sort((a, b) => b.viewCount - a.viewCount);
}
}
class LikeSort implements PostSortStrategy {
sort(posts: Post[]): Post[] {
return [...posts].sort((a, b) => b.likeCount - a.likeCount);
}
}
// 3. 컨텍스트 클래스
class PostSorter {
constructor(private strategy: PostSortStrategy) {}
setStrategy(strategy: PostSortStrategy) {
this.strategy = strategy;
}
sort(posts: Post[]): Post[] {
return this.strategy.sort(posts);
}
}
실제 사용 예시 (React 컴포넌트)
const sortMap = {
latest: new LatestSort(),
views: new ViewSort(),
likes: new LikeSort(),
};
function PostList({ posts }: { posts: Post[] }) {
const [sortType, setSortType] = useState<keyof typeof sortMap>("latest");
const sorter = new PostSorter(sortMap[sortType]);
const sortedPosts = sorter.sort(posts);
return (
<div>
<select onChange={(e) => setSortType(e.target.value as keyof typeof sortMap)}>
<option value="latest">최신순</option>
<option value="views">조회수순</option>
<option value="likes">좋아요순</option>
</select>
{sortedPosts.map((post) => (
<PostCard key={post.id} post={post} />
))}
</div>
);
}
객체지향 개념 정리
| 개념 | 예시 |
| 캡슐화 | sort 메서드 내부 구현은 외부에서 몰라도 됨 |
| 추상화 | PostSortStrategy 인터페이스로 전략들의 공통 구조 정의 |
| 다형성 | LatestSort, ViewSort, LikeSort 모두 sort()를 다르게 구현 |
| 유지보수성 | 필터 전략 추가 시 if 조건문 없이 클래스 하나만 추가하면 끝 |
'개발 공부' 카테고리의 다른 글
| SCSS 중첩 선택자: .parent { .children {} } 와 & > .children {}의 차이 (0) | 2025.05.18 |
|---|---|
| 얕은 복사, 깊은 복사 (1) | 2025.05.10 |
| 콜백 지옥 (Callback Hell) (0) | 2025.04.29 |
| 정규화(Normalization) (0) | 2025.04.27 |
| 웹소켓(WebSocket) (1) | 2025.04.04 |