CS 면접 질문 대비해서 CS 공부를 하고자한다.
공부할 때, 참고하는 책은 "면접을 위한 CS 전공지식 노트"이다.
한 단원씩 읽고, 메모장으로 정리하고 다음날 다시 정리해보면서 공부할 예정이다.
하나의 클래스에서 오직 하나의 인스턴스만 가지는 패턴이다.
하나의 클래스에서 여러개의 개별적인 인스턴스를 만들어낼 수 있지만, 하나만 생성해서 관리하는 것이다.
ex) 데이터베이스 연결
클래스, 객체, 인스턴스
1. 클래스: 변수와 메서드를 가지는 명세서, 설계도
2. 객체: 클래스로 구현할 어떤 것
3. 인스턴스: 객체를 실체화 시킨 것
인스턴스를 하나만 생성해서 관리하니깐, 구현과 유지보수가 쉬워보이지만 아니다.
단점은 다음과 같다.
DI (Dependency Injection)
메인 모듈이 직접 하위 모듈에 의존성을 주기보단, 별도의 의존성 주입자 (Dependency Injecter)가 이 부분을 가로채 메인 모듈이 간접적으로 의존성을 주입하는 방식
- A가 B에 의존성이 있다는 것은 B의 변경 사항에 A도 변해야 된다는 것을 의미
참고
SOLID에서 DIP (의존성 주입 원칙)
상위 모듈은 하위 모듈에서 어떠한 것도 가져오지 않아야 한다.
둘 다 추상화에 의존! 추상화는 세부 사항에 의존하지 말아야 한다.
다른 여러가지 방법도 많지만, 여기까지 정리하겠다.
객체를 생성하는 부분을 따로 분리하여 구현하는 패턴이다.
상속관계에 있는 두 클래스에서 상위 클래스는 객체 생성에 필요한 중요한 뼈대, 하위 클래스에서는 객체 생성에 관한 구체적인 내용을 결정한다.
팩토리 패턴 종류
1. 단순 팩토리 패턴
2. 팩토리 메서드 패턴
3. 추상 팩토리 패턴
enum CoffeeType {
LATTE,
ESPRESSO
}
abstract class Coffee {
protected String name;
public String getName() {
return name
}
}
class Latte extends Coffee {
public Latte() {
name = "latte";
}
}
class Espresso extends Coffee {
public Espresso() {
name = "Espresso"
}
}
class CoffeeFactory {
public static Coffee createCoffee(CoffeeType type) {
switch (type) {
case LATTE:
return new Latte();
case ESPRESSO:
return new Espresso();
default:
throw new IllegalArgumentException("Invalid coffee type: " + type);
}
}
}
public class Main {
public static void main(String[] args) {
Coffee coffee = CoffeeFactory.createCoffee(CoffeeType.LATTE);
System.out.println(coffee.getName()); // latte
}
}
팩토리 밑에 객체 뼈대를 담당하는 상위 클래스를 두고, 해당 클래스를 상속하여 객체 생성을 담당하는 하위 클래스를 두어 구현한다.
팩토리에서는 생성할 객체의 타입을 받아 분기처리해서 객체를 반환한다.
그래서 팩토리 메서드 패턴과 추상 팩토리 패턴이 나왔다.
| 구분 | 설명 | 장점 | 단점 |
|---|---|---|---|
| 팩토리 메서드 패턴 | 객체 생성 책임을 하위(서브) 팩토리 클래스에 위임 | - 새로운 클래스 추가·제거 시 클라이언트 코드 변경 없음 - 객체 생성과 사용 분리로 유연성 향상 | - 객체 종류 증가 시 팩토리 클래스 수도 증가 - 구조 복잡도 상승 |
| 추상 팩토리 패턴 | 관련된 객체의 그룹(제품군) 을 생성하는 인터페이스 제공 | - 제품군 일관성 유지 - 객체 생성 책임 분산으로 단일 팩토리의 과부하 방지 | - 구현 복잡 - 코드량 증가 |
정책 패턴이라고도 하며, 객체의 행위를 바꾸고 싶은 경우 직접 수정하지 않고, 전략이라고 부르는 캡슐화한 알고리즘을 컨텍스트 안에서 바꿔주면서 상호 교체가 가능하게 만드는 패턴
ex) 결제 시스템 (카카오페이? 토스페이?), 로그인 방식 (서비스 내? OAuth?)
주체가 어떤 객체의 상태 변화를 관찰하다가 상태 변화가 있을 때마다 메서드 등을 통해 옵저버 목록에 있는 옵저버들에게 변화를 알려주는 패턴
주체: 객체의 상태 변화를 보고 있는 관찰자
옵저버: 객체 상태 변화에 따라 추가 변화가 생기는 객체들
ex) 유튜브 알림, 트위터 알림, MVC
- MVC: 모델에서 변경 사항이 생겨, update로 뷰에 알려주고 컨트롤러가 작동한다.
- 주체와 객체를 분리하는 경우와 한번에 두는 경우가 있다.
import java.util.ArrayList;
import java.util.List;
interface Subject {
public void register(Observer obj);
public void unregister(Observer obj);
public void notifyObservers();
public Object getUpdate(Observer obj);
}
interface Observer {
public void update();
}
class Topic implements Subject {
private List<Observer> observers;
private String massage;
public Topic() {
this.observers = new ArrayList<>();
this.message = "";
}
@Override
public void register(Observer obj) {
if (!observers.contains(obj)) observers.add(obj);
}
@Override
public void unregister(Observer obj) {
observers.remove(obj);
}
@Override
public void notifyObservers() {
this.observers.forEach(Observer::update);
}
@Override
public Object getUpdate(Observer obj) {
return this.message;
}
public void postMessage(String msg) {
System.out.println("Message sended to Topic: " + msg);
this.message = msg;
notifyObservers(); // 옵저버에게 상태변화 알림
}
}
class TopicSubscriber implements Observer {
private String name;
private Subject topic;
public TopicSubscriber(String name, Subject topic) {
this.name = name;
this.topic = topic;
}
// 구독중인 옵저버들에게 상태 변화에 대한 알림이 감
@Override
public void update() {
String msg = (String) topic.getUpdate(this);
System.out.println(name + ":: got message >> " + msg);
}
}
public class HelloWorld {
public static void main(String[] args) {
Topic topic = new Topic();
Observer a = new TopicSubscriber("a", topic);
Observer b = new TopicSubscriber("b", topic);
Observer c = new TopicSubscriber("c", topic);
topic.register(a);
topic.register(b);
topic.register(c);
topic.postMessage("hello everyone!");
}
}
/*
Message sended to Topic: hello everyone!
a:: got message >> hello everyone!
b:: got message >> hello everyone!
c:: got message >> hello everyone!
*/
자바 상속과 구현
1. 상속(extends): 자식 클래스가 부모 클래스의 메서드 등을 상속받아 사용하며 자식 클래스에서 추가 및 확장을 할 수 있는 것을 말한다. 이를 통해 재사용성, 중복성의 최소화가 이루어 짐
2. 구현(implements): 부모 인터페이스를 자식 클래스에서 재정의하여 구현하는 것, 상속과는 달리 반드시 부모클래스의 메서드를 재정의하여 구현해야한다.
- 상속은 일반 클래스, abstract 클래스 기반으로 구현, 구현은 인터페이스 기반으로 구현
대상 객체에 접근하기 전에 그 접근에 대한 흐름을 가로채 해당 접근을 필터링하거나 수정하는 역할을 하는 계층이 있는 패턴
1. 객체의 속성, 변환 등을 보완
2. 보안, 데이터 검증, 캐싱, 로깅에 사용
서버와 클라이언트 사이에서 클라이언트가 자신을 통해 다른 네트워크 서비스에 간접적으로 접속할 수 있게 해주는 컴퓨터 시스템이나 응용 프로그램
예시
1. nginx: 비동기 이벤트 기반의 구조와 다수의 연결을 효과적으로 처리 가능한 웹 서버
-> 서버 앞단의 프록시 서버로 활용
-> 실제 포트를 숨기고, 정적 자원 gzip으로 압축, 메인 서버 앞단에서 로깅 가능
2. CloudFlare: 웹 서버 앞단에 프록시 서버로 두어 DDOS 공격 방어나 HTTPS 구축에 쓰인다.
-> 의심스러운 트래픽은 CAPTCHA를 기반으로 일정부분 막아줌
3. CDN :각 사용자가 인터넷에 접속하는 곳과 가까운 곳에서 콘텐츠를 캐싱 또는 배포하는 서버 네트워크
-> 이를 통해 웹 서버로 부터 콘텐츠를 다운로드하는 시간을 줄일 수 있음
CORS(Cross-Origin Resource Sharing): 서버가 웹 브라우저에서 리소스를 로드할 때, 다른 오리진을 통해 로드하지 못하게 하는 HTTP 헤더 기반 메커니즘
프론트엔드 포트와 백엔드 포트가 다르면 테스팅 중 CORS 에러가 난다.
프록시 서버를 통해 프론트엔드에서 요청되는 오리진을 동일하게 수정해주면 테스팅이 진행이 된다.
이터레이터를 사용하여 컬렉션의 요소들에 접근하는 패턴이다.
순회할 수 있는 여러 가지 자료형의 구조와 상관없이 이터레이터라는 하나의 인터페이스로 순회가 가능
즉시 실행 함수를 통해 private, public 같은 접근제어자를 만드는 패턴
ex) 자바스크립트는 접근 제어자가 없어, 전역 범위에서 스크립트가 실행된다.
-> 해당 패턴으로 접근 제어자를 구현하기도 한다.
접근 제어자 정리
1. public: 클래스에 정의된 함수에서 접근 가능, 자식 클래스와 외부 클래스에서 접근 가능
2. protected: 클래스에 정의된 함수에서 접근 가능, 자식 클래스는 접근 가능, 외부 클래스는 접근 불가
3. private: 클래스에 정의된 함수에서 접근 가능, 자식 클래스와 외부 클래스에서 접근 불가
4. 즉시 실행 함수: 함수를 정의하자마자 바로 호출하는 함수, 초기화 코드, 라이브러리 내 전역 변수의 충돌 방지 등에 사용한다.
Model, View, Controller로 이루어진 패턴
애플리케이션의 구성 요소를 세 가지 역할로 구분하여 개발 프로세스에서 각각의 구성 요소에만 집중해서 개발할 수 있다.
Spring: 디스패처 서블릿의 요청 처리 과정을 이해해보자...
MVC로부터 파생된 패턴
- MVP 패턴
- MVC의 Controller가 Presenter로 교체된 패턴
- 뷰와 프레젠터는 일대일 관계여서 MVC 패턴보다 더 강한 결합을 지닌 패턴
- MVVM 패턴
- MVC의 Controller가 뷰모델(view model)로 바뀐 패턴
- 뷰모델은 뷰를 더 추상화한 계층, MVC패턴과 다르게 커맨드와 데이터 바인딩을 가짐
- 뷰와 뷰모델 사이의 양방향 데이터 바인딩 지원, UI를 별도의 코드 수정 없이 재사용가능, 단위 테스팅하기 쉽다는 장점
- 값 대입만으로 변수가 변경, 양방향 바인딩, html을 토대로 컴포넌트를 구축, 재사용 가능한 컴포넌트를 기반으로 UI를 구축가능 (Vue.js)