728x90
반응형

1. 의존성의 정의

  • A가 B에 의존한다는 것은 A가 B를 필요로 한다는 의미
  • 프로그래밍에서는 "A가 동작하기 위해 B가 필요하다"로 해석

의존성은 일상생활에서 흔히 사용되는 개념을 프로그래밍 세계로 가져온 것입니다. 예를 들어, 자바 코드에서 Ladder 클래스가 Line과 ArrayList에 의존한다고 할 때, 이는 Ladder 클래스가 제대로 동작하기 위해서는 Line과 ArrayList가 필요하다는 의미입니다. 더 넓게 보면, 클래스 A가 필드, 생성자, 메서드 등에서 사용하는 모든 클래스에 의존한다고 볼 수 있습니다.

 

2. 의존성 주입의 정의

  • 의존성 주입은 객체가 동작하기 위해 필요한 것을 외부에서 객체 내부로 집어넣는 것
  • 주입은 객체 안에 프로그래밍 요소나 다른 객체를 넣는 행위

의존성 주입은 객체지향 프로그래밍에서 중요한 개념입니다. 여기서 '주입'이란 단어는 사전적 의미인 "어떤 물체 안에 액체나 기체를 넣는 것"에서 유래했습니다. 프로그래밍 맥락에서는 이를 "객체 안에 필요한 요소를 넣는 것"으로 해석할 수 있습니다. 따라서 의존성 주입은 객체가 필요로 하는 다른 객체나 값을 외부에서 제공하는 기법이라고 할 수 있습니다.

 

3. 의존성 주입 방법

  • 자바에서는 주로 세 가지 방법으로 의존성 주입을 수행
  • 생성자 주입, 필드 주입, 메서드(setter) 주입
  • 필드 주입은 일반적으로 권장되지 않음

자바에서 의존성 주입을 구현하는 방법은 크게 세 가지입니다. 생성자를 통한 주입, 필드를 직접 설정하는 방법, 그리고 setter 메서드를 이용하는 방법입니다. 이 중 필드를 직접 설정하는 방법은 캡슐화를 깨뜨리기 때문에 일반적으로 권장되지 않습니다. 따라서 주로 생성자 주입과 setter 주입이 사용됩니다.

 

4. 의존성 주입의 장점

  • 의존 대상을 쉽게 변경할 수 있음
  • 테스트가 용이해짐
  • 코드의 유연성과 재사용성이 증가

의존성 주입의 가장 큰 장점은 의존 대상을 쉽게 변경할 수 있다는 점입니다. 예를 들어, 테스트 시에는 실제 객체 대신 mock 객체를 주입할 수 있어 테스트가 훨씬 쉬워집니다. 또한, 요구사항이 변경되거나 버그 수정이 필요할 때도 의존 객체만 교체함으로써 쉽게 대응할 수 있습니다. 이는 코드의 유연성과 재사용성을 크게 향상시킵니다.

 

5. 의존성 주입의 단점

  • 세부 구현을 외부에 노출시킬 수 있음
  • 캡슐화를 깰 수 있는 위험이 있음
  • 코드의 복잡성이 증가할 수 있음

의존성 주입의 주요 단점은 객체의 세부 구현을 외부에 노출시킬 수 있다는 점입니다. 예를 들어, 카드 게임에서 Deck 클래스의 카드 선택 전략을 외부에서 주입받는다면, 이는 Deck의 내부 동작을 외부에 노출시키는 것이 될 수 있습니다. 이는 캡슐화 원칙을 위반할 수 있는 위험이 있습니다. 또한, 의존성 주입을 위해 추가되는 매개변수나 메서드로 인해 코드의 복잡성이 증가할 수 있습니다.

 

6. 결론

의존성 주입은 객체 지향 프로그래밍에서 중요한 기법으로, 코드의 유연성과 테스트 용이성을 크게 향상시킬 수 있습니다. 그러나 동시에 캡슐화를 깨뜨릴 수 있는 위험이 있으므로, 이를 적절히 사용하기 위해서는 신중한 설계가 필요합니다. 개발자는 의존성 주입의 장단점을 잘 이해하고, 상황에 맞게 적절히 활용해야 합니다.

728x90
반응형
728x90
반응형

1. HTTP vs HTTPS

웹 통신의 기본 프로토콜인 HTTP(Hypertext Transfer Protocol)는 클라이언트와 서버 간의 데이터 교환을 위한 기본적인 규칙을 제공합니다. 그러나 HTTP는 보안 측면에서 몇 가지 중요한 취약점을 가지고 있습니다. 이러한 취약점을 해결하기 위해 개발된 것이 바로 HTTPS(HTTP Secure)입니다.

HTTPS는 기존 HTTP 프로토콜에 보안 계층을 추가한 것으로, 주로 SSL(Secure Sockets Layer) 또는 그 후속 버전인 TLS(Transport Layer Security)를 사용합니다. 이를 통해 데이터의 기밀성, 완전성, 그리고 통신 상대방 인증 측면에서 큰 개선을 이루었습니다.

2. HTTP의 보안 취약점

HTTP가 가진 주요 보안 취약점은 다음과 같습니다:

a) 암호화 기능 부재:
   HTTP는 데이터를 평문으로 전송합니다. 이는 누구나 네트워크 트래픽을 감시하면 통신 내용을 쉽게 볼 수 있다는 것을 의미합니다. 예를 들어, 공공 Wi-Fi에서 HTTP를 사용하면 비밀번호나 신용카드 정보 같은 민감한 데이터가 노출될 위험이 있습니다.

b) 메시지 변경 감지 불가
   HTTP는 전송 중 데이터가 변경되었는지 확인할 방법이 없습니다. 즉, 중간자 공격(man-in-the-middle attack)에 취약합니다. 악의적인 제3자가 통신을 가로채 내용을 변경해도 수신자는 이를 알아차리기 어렵습니다.

c) 통신 상대 인증 불가
   HTTP는 통신 상대방이 진짜 의도한 서버나 클라이언트인지 확인할 수 없습니다. 이는 피싱 사이트 같은 위장 서버나 악의적인 클라이언트의 요청을 구분하기 어렵다는 것을 의미합니다.

3. HTTPS의 구조
HTTPS는 이러한 HTTP의 취약점을 해결하기 위해 HTTP와 TCP 사이에 보안 계층을 추가합니다. 이 보안 계층은 SSL/TLS 프로토콜을 사용합니다.

SSL과 TLS는 본질적으로 같은 프로토콜의 다른 버전입니다. TLS가 SSL의 더 최신 버전이지만,

일반적으로 'SSL'이라는 용어가 더 널리 사용됩니다. 이 프로토콜은 다음과 같은 보안 기능을 제공합니다
- 데이터 암호화
- 데이터 무결성 검증
- 서버 (그리고 선택적으로 클라이언트) 인증

4. 암호화 방식: 대칭키와 비대칭키
HTTPS는 두 가지 주요 암호화 방식을 사용합니다:


a) 대칭키 암호화:
   - 동일한 키를 사용하여 암호화와 복호화를 수행합니다.
   - 장점: 암호화와 복호화 속도가 빠릅니다.
   - 단점: 키를 안전하게 공유하는 것이 어렵습니다. 처음에 키를 전달할 때 탈취 당하면 모든 통신이 위험해집니다.

b) 비대칭키 암호화 (공개키 암호화)
   - 두 개의 다른 키(공개키와 개인키)를 사용합니다.
   - 공개키로 암호화한 데이터는 개인키로만 복호화할 수 있습니다.
   - 장점: 키 교환의 안전성이 높습니다.
   - 단점: 대칭키 방식에 비해 암호화/복호화 속도가 느립니다.

HTTPS는 이 두 방식의 장점을 활용하기 위해 하이브리드 방식을 사용합니다
1. 처음에 비대칭키 암호화를 사용하여 안전하게 대칭키를 교환합니다.
2. 이후 실제 데이터 통신은 빠른 대칭키 암호화를 사용합니다.

5. 인증서와 CA(Certificate Authority)
HTTPS에서 중요한 또 다른 요소는 디지털 인증서입니다. 인증서는 서버의 신원을 보증하는 역할을 합니다. 이 인증서는 신뢰할 수 있는 제3자 기관인 CA(Certificate Authority)에서 발급합니다.

CA의 역할:
- 서버의 신원을 확인합니다.
- 서버의 공개키를 포함한 인증서를 발급합니다.
- 인증서에 디지털 서명을 추가하여 인증서의 진위를 보증합니다.

클라이언트(예: 웹 브라우저)는 서버가 제시한 인증서를 CA의 공개키를 사용하여 검증합니다. 이를 통해 서버의 신원과 공개키의 신뢰성을 확인할 수 있습니다.

6. HTTPS 통신의 흐름
HTTPS 통신은 다음과 같은 단계로 이루어집니다
1) 클라이언트가 서버에 연결을 요청합니다.
2) 서버는 자신의 SSL 인증서를 클라이언트에게 전송합니다.
3) 클라이언트는 받은 인증서를 확인합니다:
   - 인증서가 신뢰할 수 있는 CA에 의해 발급되었는지 확인
   - 인증서가 유효한지 (만료되지 않았는지) 확인
   - 인증서의 도메인 이름이 접속하려는 웹사이트와 일치하는지 확인
4) 인증서가 유효하다고 판단되면, 클라이언트는 대칭키(세션 키)를 생성합니다.
5) 클라이언트는 이 세션 키를 서버의 공개키로 암호화하여 서버에 전송합니다.
6) 서버는 자신의 개인키를 사용하여 암호화된 세션 키를 복호화합니다.
7) 이제 클라이언트와 서버는 동일한 세션 키를 공유하게 되었고, 이를 사용하여 안전한 대칭키 암호화 통신을 시작합니다.

7. 결론
HTTPS는 SSL/TLS를 통해 HTTP의 보안 취약점을 효과적으로 보완합니다. 대칭키와 비대칭키 암호화의 장점을 결합하고, CA의 인증 시스템을 활용하여 안전한 통신 환경을 제공합니다. 이를 통해 데이터의 기밀성(암호화), 무결성(변조 방지), 그리고 통신 상대방의 신원(인증)을 보장할 수 있습니다.

HTTPS의 사용은 온라인 뱅킹, 전자상거래, 개인정보 처리 등 민감한 정보를 다루는 웹사이트에서 특히 중요하며, 현대 웹의 필수적인 요소로 자리잡았습니다. 사용자의 개인정보 보호와 안전한 온라인 경험을 위해 HTTPS의 중요성은 계속해서 증가하고 있습니다.

728x90
반응형
728x90
반응형

데드락이란 무엇인가?

데드락(Deadlock)은 두 개 이상의 프로세스나 스레드가 서로가 가진 자원을 기다리며 무한히 대기하는 상태(=교착상태)

(외나무 다리의 양 끝에서 서로 비켜주기를 바라는 것과 같은 상태)

 

데드락의 발생 조건

데드락이 발생하기 위해서는 다음 네 가지 조건이 동시에 충족되어야 합니다:

  1. 상호 배제(Mutual Exclusion): 자원은 한 번에 하나의 프로세스만 사용할 수 있어야 합니다.
  2. 점유 대기(Hold and Wait): 자원을 가지고 있는 프로세스가 다른 자원을 기다리고 있어야 합니다.
  3. 비선점(No Preemption): 다른 프로세스가 사용 중인 자원을 강제로 빼앗을 수 없어야 합니다.
  4. 순환 대기(Circular Wait): 프로세스들이 순환 형태로 서로의 자원을 기다리고 있어야 합니다.

 

DB에서 발생하는 데드락 예시

-- Transaction A
START TRANSACTION;
UPDATE accounts SET balance = balance - 100 WHERE id = 1;
UPDATE accounts SET balance = balance + 100 WHERE id = 2;
COMMIT;

-- Transaction B
START TRANSACTION;
UPDATE accounts SET balance = balance - 200 WHERE id = 2;
UPDATE accounts SET balance = balance + 200 WHERE id = 1;
COMMIT;

 

이 두 트랜잭션이 다음과 같은 순서로 실행된다고 가정해 봅시다:

  1. Transaction A가 id=1인 계좌의 잠금을 획득합니다.
  2. Transaction B가 id=2인 계좌의 잠금을 획득합니다.
  3. Transaction A가 id=2인 계좌의 잠금을 시도하지만, B가 이미 잠금을 가지고 있어 대기합니다.
  4. Transaction B가 id=1인 계좌의 잠금을 시도하지만, A가 이미 잠금을 가지고 있어 대기합니다.

이 상황에서 두 트랜잭션은 서로가 가진 잠금을 기다리며 영원히 진행하지 못하는 데드락 상태에 빠지게 됩니다.

MySQL은 이러한 데드락을 감지하면 둘 중 하나의 트랜잭션을 강제로 중단시켜 문제를 해결합니다. 하지만 이는 애플리케이션 로직에 예기치 않은 오류를 발생시킬 수 있습니다.

 

데드락을 방지하기 위한 방법

  1. 트랜잭션의 범위를 최소화하여 잠금 시간을 줄입니다.
  2. 모든 트랜잭션이 같은 순서로 테이블에 접근하도록 합니다.
  3. 가능하다면 SELECT ... FOR UPDATE 대신 낙관적 잠금(Optimistic Locking)을 사용합니다.
  4. 트랜잭션 격리 수준을 적절히 조정합니다.

 

결론

데드락은 동시성 프로그래밍에서 흔히 발생할 수 있는 문제입니다. 개발자는 데드락의 발생 조건을 이해하고, 적절한 설계와 프로그래밍 기법을 통해 이를 예방해야 합니다. 특히 데이터베이스 작업에서는 트랜잭션의 설계와 실행 순서에 주의를 기울여야 합니다. 데드락을 완전히 피하는 것은 어렵지만, 그 위험을 최소화하고 발생 시 적절히 대응할 수 있도록 준비하는 것이 중요합니다.

728x90
반응형
728x90
반응형

디자인 패턴이란?

개념 설명: 디자인 패턴은 소프트웨어 개발에서 반복적으로 발생하는 문제들에 대한 일반적이고 재사용 가능한 해결책입니다. 이는 특정 상황(컨텍스트)에서 발생하는 문제에 대한 검증된 해결 방법을 제공합니다.

 

쉬운 설명: 디자인 패턴은 마치 요리 레시피와 같습니다. 특정 요리를 만들 때 따르는 단계별 지침처럼, 디자인 패턴은 특정 프로그래밍 문제를 해결하는 데 사용할 수 있는 검증된 방법을 제공합니다.

 

예시: 일상생활에서의 디자인 패턴을 생각해봅시다. 예를 들어, "리마인더 패턴"이라는 것을 상상해볼 수 있습니다. 출입증을 자주 두고 나가는 문제가 있다면, 문 앞에 "출입증 확인" 스티커를 붙이는 것이 해결책이 될 수 있습니다. 이는 반복적인 문제(출입증을 잊어버림)에 대한 간단하고 효과적인 해결책입니다.

 

디자인 패턴의 중요성

  • 재사용성: 다양한 상황에서 적용 가능한 해결책을 제공합니다.
  • 소통의 용이성: 개발자들 간의 의사소통을 더욱 효율적으로 만듭니다.
  • 올바른 설계 촉진: 초보 개발자들이 좋은 설계 방식을 빠르게 학습하는 데 도움을 줍니다.

 

디자인 패턴의 예: 전략 패턴

 

개념 설명: 전략 패턴은 알고리즘군을 정의하고 각각을 캡슐화하여 교환해서 사용할 수 있도록 만드는 패턴입니다.

쉬운 설명: 전략 패턴은 마치 스마트폰의 케이스를 바꾸는 것과 같습니다. 스마트폰(기본 객체)은 그대로 두고, 필요에 따라 다른 케이스(전략)를 끼워 사용할 수 있습니다.

예시: 자동차 경주 게임을 만든다고 가정해봅시다. 여기서 자동차의 움직임 방식이 다양할 수 있습니다:

  • 항상 움직이는 자동차
  • 항상 멈춰있는 자동차
  • 특정 조건에서만 움직이는 자동차

이럴 때 전략 패턴을 사용하면, 자동차 객체는 그대로 두고 움직임 전략만 교체하여 다양한 동작을 구현할 수 있습니다.

 

디자인 패턴 학습에 대한 조언

  • 기본기 중요: 객체 지향 프로그래밍의 기본 원리를 먼저 잘 이해하는 것이 중요합니다.
  • 단순성 추구: 패턴을 위한 패턴 적용보다는, 현재 상황에서 가장 단순한 해결책을 찾는 것이 중요합니다.
  • 종합적 이해: 패턴의 사용 방법뿐만 아니라, 그 패턴의 기반 원리, 적용 시기, 장단점 등을 종합적으로 이해해야 합니다.

결론

디자인 패턴은 개발자의 강력한 도구이지만, 모든 문제의 만능 해결책은 아닙니다. 상황에 맞게 적절히 사용할 때 그 진가를 발휘합니다. 기본기를 탄탄히 다지고, 실제 문제 해결 과정에서 패턴을 적용해보며 경험을 쌓는 것이 중요합니다.

이렇게 구성하면 독자들이 디자인 패턴의 개념, 중요성, 예시, 그리고 학습 방법에 대해 쉽게 이해할 수 있을 것입니다.

728x90
반응형
728x90
반응형

단일 책임 원칙 (Single Responsibility Principle, SRP)

개념: 하나의 클래스는 하나의 책임만 가져야 한다.

쉬운 설명: 각 부품은 한 가지 일만 잘하면 된다.

일상 예시: 주방에서 칼은 자르는 일만, 냄비는 음식을 담아 조리하는 일만 합니다. 칼에 음식을 담아 끓이려 하거나, 냄비로 음식을 자르려 하지 않죠. 각자의 역할에 충실한 것이 단일 책임 원칙과 비슷합니다.

개방-폐쇄 원칙 (Open-Closed Principle, OCP)

개념: 소프트웨어 개체(클래스, 모듈, 함수 등)는 확장에는 열려 있어야 하고, 변경에는 닫혀 있어야 한다.

쉬운 설명: 기존 코드를 건드리지 않고도 새로운 기능을 추가할 수 있어야 한다.

일상 예시: 스마트폰을 생각해보세요. 새로운 앱을 설치할 때 휴대폰의 운영체제를 변경하지 않습니다. 휴대폰(기존 시스템)은 그대로 두고, 새로운 기능(앱)을 쉽게 추가할 수 있죠. 이것이 바로 개방-폐쇄 원칙의 예입니다.

 

리스코프 치환 원칙 (Liskov Substitution Principle, LSP)

개념: 프로그램의 객체는 프로그램의 정확성을 깨뜨리지 않으면서 하위 타입의 인스턴스로 바꿀 수 있어야 한다.

쉬운 설명: 부모 클래스가 들어갈 자리에 자식 클래스를 넣어도 잘 작동해야 한다.

일상 예시: 커피숍에서 일하는 바리스타를 생각해보세요. 숙련된 바리스타 대신 신입 바리스타가 와도 커피를 만들 수 있어야 합니다. 물론 맛의 차이는 있겠지만, 기본적인 커피 제조 과정은 동일하게 수행할 수 있어야 하죠. 이처럼 대체 가능성을 보장하는 것이 리스코프 치환 원칙입니다.

 

인터페이스 분리 원칙 (Interface Segregation Principle, ISP)

개념: 클라이언트는 자신이 사용하지 않는 메서드에 의존 관계를 맺으면 안 된다.

쉬운 설명: 불필요한 인터페이스는 구현하지 말고, 꼭 필요한 인터페이스만 구현하라.

일상 예시: 스마트폰의 리모컨 앱을 생각해보세요. TV 리모컨 앱은 TV 제어 기능만 있고, 에어컨 리모컨 앱은 에어컨 제어 기능만 있습니다. 하나의 앱에 모든 가전제품을 제어하는 기능을 넣지 않죠. 이처럼 필요한 기능만 분리해서 제공하는 것이 인터페이스 분리 원칙과 유사합니다.

 

의존관계 역전 원칙 (Dependency Inversion Principle, DIP)

개념: 고수준 모듈은 저수준 모듈의 구현에 의존해서는 안 되며, 저수준 모듈이 고수준 모듈에서 정의한 추상 타입에 의존해야 한다.

쉬운 설명: 구체적인 것이 추상적인 것에 의존해야 하며, 변하기 쉬운 것보단 변하기 어려운 것에 의존해야 한다.

일상 예시: 전기 콘센트를 생각해보세요. 콘센트는 어떤 전자기기가 꽂힐지 모르는 추상적인 인터페이스입니다. 전자기기(구체적인 것)는 이 추상적인 콘센트 규격(추상화)에 맞춰 만들어집니다. 콘센트 디자인이 변경되면 모든 전자기기를 바꿔야 하는 상황은 피해야 하죠. 이처럼 구체적인 것이 추상적인 것에 의존하게 만드는 것이 의존관계 역전 원칙입니다.

 

이렇게 SOLID 원칙을 적용하면, 코드의 유지보수가 쉬워지고, 확장성이 높아지며, 재사용성이 증가합니다. 마치 잘 정돈된 주방에서 요리하는 것처럼, SOLID 원칙을 따르는 코드는 개발자가 더 효율적으로 일할 수 있는 환경을 제공합니다.

728x90
반응형
728x90
반응형

서론

객체지향 프로그래밍에서 '추상화 수준'은 코드의 가독성과 유지보수성을 크게 향상시킬 수 있는 중요한 개념입니다. 이 글에서는 추상화 수준이 무엇인지, 왜 중요한지, 그리고 어떻게 적용할 수 있는지 살펴보겠습니다.

 

객체지향 프로그래밍 

객체지향 프로그래밍(OOP)은 소프트웨어 개발의 주요 패러다임 중 하나로, 프로그램을 객체들의 모임으로 구조화하는 프로그래밍 방식입니다. 이 방식은 실제 세계를 모델링하여 소프트웨어를 설계하고 구현하는 데 효과적입니다.

 

객체지향의 핵심 개념

a) 객체(Object): 데이터와 그 데이터를 조작하는 메서드를 하나의 단위로 묶은 것.

b) 클래스(Class): 객체를 생성하기 위한 템플릿 또는 청사진.

c) 캡슐화(Encapsulation): 객체의 내부 구현을 숨기고 외부에서는 정의된 인터페이스로만 접근할 수 있게 하는 개념.

d) 상속(Inheritance): 기존 클래스의 특성을 새로운 클래스가 물려받아 재사용성을 높이는 메커니즘.

e) 다형성(Polymorphism): 같은 인터페이스나 메서드가 다양한 형태로 동작할 수 있게 하는 개념.

 

객체지향의 장점

  • 코드 재사용성 증가
  • 유지보수의 용이성
  • 대규모 프로젝트에 적합한 구조
  • 실제 세계를 모델링하기 쉬움

객체지향과 추상화

객체지향 프로그래밍에서 추상화는 복잡한 시스템을 단순화하고 필수적인 특징만을 강조하는 과정입니다. 이는 불필요한 세부사항을 제거하고 문제의 본질에 집중할 수 있게 해줍니다.

추상화는 인터페이스와 추상 클래스를 통해 구현될 수 있으며, 이를 통해 코드의 유연성과 확장성을 높일 수 있습니다.

추상화 수준과 객체지향

추상화 수준은 객체지향 설계의 품질을 결정짓는 중요한 요소입니다. 적절한 추상화 수준을 유지함으로써, 우리는 객체들 간의 관계를 더 명확히 정의하고, 각 객체의 책임을 적절히 분배할 수 있습니다.

 

추상화와 추상화 수준이란?

추상화는 복잡한 시스템에서 핵심적인 부분만을 골라내는 과정입니다. 객체지향 언어에서는 객체의 공통된 특성을 뽑아 일반화하는 것을 의미합니다.

추상화 수준은 객체나 메서드가 얼마나 일반적이거나 구체적인지를 나타내는 정도입니다. 높은 추상화 수준은 더 일반적이고, 낮은 추상화 수준은 더 구체적입니다.

 

추상화 수준의 중요성

  • 코드 가독성 향상: 같은 추상화 수준의 코드는 읽기 쉽습니다.
  • 유지보수성 개선: 각 객체나 메서드가 한 가지 일만 담당하게 되어 수정이 용이합니다.
  • 테스트 용이성: 작은 단위로 나누어져 있어 단위 테스트가 쉬워집니다.
  • 재사용성 증가: 잘 나누어진 기능은 다른 곳에서도 쉽게 재사용할 수 있습니다.

실제 예시 : 쇼핑시나리오

낮은 수준의 추상화 코드

public void goShopping() {
    walkToStore();
    grabShoppingCart();
    findMilk();
    findEggs();
    goToCheckout();
    payForItems();
    walkHome();
}

이 코드는 너무 구체적이고 한 메서드가 여러 가지 일을 하고 있습니다.

 

높은 수준의 추상화 코드

public void goShopping() {
    Store store = new Store();
    ShoppingCart cart = store.getShoppingCart();
    List<Item> items = store.getItems(Arrays.asList("milk", "eggs"));
    cart.addItems(items);
    store.checkout(cart);
}

이 코드는 각 객체가 자신의 책임만 수행하도록 되어 있어 더 읽기 쉽고 유지보수하기 좋습니다.

 

실제 예시 : 자동차 경주

낮은 수준의 추상화 코드

class Car {
    private int position = 0;
    private IntSupplier powerGenerator;

    public void moveOnGeneratedPowerExceedsThreshold() {
        if (powerGenerator.getAsInt() > 4) {
            position++;
        }
    }
}

 

높은 수준의 추상화 코드

class Car {
    private Position position;
    private PowerGenerator powerGenerator;

    public void move() {
        if (powerGenerator.generatePower() > PowerThreshold.MOVE_THRESHOLD) {
            position.increase();
        }
    }
}

class Position {
    private int value = 0;
    public void increase() {
        value++;
    }
}

interface PowerGenerator {
    int generatePower();
}

 

추상화 수준 개선하기

a) 메서드 추출 : 긴 메서드를 여러 개의 작은 메서드로 나눕니다.

b) 새로운 클래스 도출 : 관련된 책임을 가진 메서드들을 새로운 클래스로 묶습니다.

c) 인터페이스 사용 : 구체적인 구현 대신 인터페이스를 사용하여 추상화 수준을 높입니다.

 

결론

추상화 수준을 일관되게 유지하는 것은 객체지향 프로그래밍의 핵심 원칙 중 하나입니다. 이를 통해 우리는 더 깔끔하고, 이해하기 쉬우며, 유지보수가 용이한 코드를 작성할 수 있습니다. 항상 "이 객체나 메서드가 한 가지 일만 하고 있는가?"를 질문하며 코드를 작성하고 리팩토링하는 습관을 들이면, 자연스럽게 좋은 추상화 수준을 유지할 수 있을 것입니다.

 

출처 : https://www.youtube.com/watch?v=1VwRtsLDiyM&list=PLgXGHBqgT2TvpJ_p9L_yZKPifgdBOzdVH&index=4

 

 

 

728x90
반응형
728x90
반응형

인덱스란 무엇인가?

인덱스는 데이터베이스에서 검색 속도를 향상시키기 위해 사용되는 자료 구조입니다. 책의 찾아보기와 유사한 역할을 하며, 원하는 정보를 빠르게 찾을 수 있게 해줍니다.

예를 들어, 도서관에서 책을 찾는 상황을 생각해 봅시다. 인덱스가 없다면 모든 책장을 일일이 살펴봐야 하지만, 도서 분류 시스템(인덱스)이 있다면 원하는 책의 위치를 빠르게 찾을 수 있습니다.

 

인덱스의 작동 원리

인덱스는 주로 B-tree(Balanced Tree) 구조를 사용합니다. B-tree의 주요 특징은 다음과 같습니다:

a) 균형 트리: 모든 리프 노드가 같은 레벨에 있어 검색 성능이 일정합니다.

b) 탐색 트리: 항상 정렬된 상태를 유지하여 빠른 검색이 가능합니다.

c) 다중 데이터 저장: 하나의 노드에 여러 데이터를 저장할 수 있어 효율적입니다.

 

B-tree를 사용하면 데이터를 로그 시간 복잡도(O(log n))로 검색할 수 있어, 전체 데이터를 스캔하는 것보다 훨씬 빠릅니다.

 

인덱스의 종류

a) 클러스터드 인덱스 (Clustered Index)

  • 테이블의 물리적 데이터 순서를 결정합니다.
  • 테이블당 하나만 존재할 수 있습니다.
  • 주로 기본키(Primary Key)에 자동으로 생성됩니다.
  • 데이터 검색이 매우 빠르지만, 데이터 삽입/수정 시 비용이 높을 수 있습니다.

b) 논클러스터드 인덱스 (Non-clustered Index)

  • 별도의 인덱스 구조를 생성합니다.
  • 하나의 테이블에 여러 개 생성이 가능합니다.
  • 인덱스 구조에 실제 데이터의 위치 정보를 저장합니다.
  • 클러스터드 인덱스에 비해 검색은 조금 느리지만, 데이터 변경에 따른 부담이 적습니다.

인덱스 사용의 실제 예시

음식점 정보를 담은 테이블에서 '서울'에 있는 음식점을 찾는 상황을 가정해 봅시다.

  • 인덱스가 없을 때: 전체 데이터(예: 100만 개)를 순차적으로 스캔하며 '서울'인 주소를 찾습니다.
  • 성능: 매우 느림 (모든 데이터를 확인해야 함)
  • 주소에 인덱스가 있을 때:
    1. 인덱스 트리에서 '서울'을 빠르게 찾습니다.
    2. 해당 데이터의 실제 위치를 확인합니다.
    3. 필요한 데이터만 접근하여 결과를 반환합니다. 성능: 매우 빠름 (필요한 데이터에만 직접 접근)

실제 12만 개의 데이터에서 인덱스 사용 전후를 비교한 결과:

  • 인덱스 사용 전: 검색 시간 약 0.44초
  • 인덥스 사용 후: 검색 시간 약 0.02초 (약 20배 성능 향상)

효과적인 인덱스 사용시기

a) 적절한 컬럼 선택:

  • WHERE, JOIN, ORDER BY 절에 자주 사용되는 컬럼
  • 중복도가 낮은 컬럼 (예: 주민등록번호, 이메일 주소)
  • 범위 검색보다는 정확한 값 검색에 사용되는 컬럼

b) 복합 인덱스 고려:

  • 여러 컬럼을 함께 사용하는 쿼리가 자주 실행된다면 복합 인덱스를 고려하세요.
  • 컬럼 순서가 중요합니다. 가장 자주 사용되는 컬럼을 첫 번째로 지정하세요.

c) 인덱스 개수 제한:

  • 너무 많은 인덱스는 오히려 성능을 저하시킬 수 있습니다.
  • 데이터 변경 작업(INSERT, UPDATE, DELETE)시 모든 인덱스를 업데이트 필요. (쓰기가 많은 경우 비추천)

d) 주기적인 인덱스 관리:

  • 사용되지 않는 인덱스는 제거하세요.
  • 필요에 따라 인덱스를 재구성하여 최적의 상태를 유지하세요.

주의사항

  • 인덱스는 검색 성능을 향상시키지만, 데이터 변경 작업의 성능은 저하시킬 수 있습니다.
  • 작은 테이블에서는 인덱스의 효과가 미미할 수 있습니다. (대규모에서 추천)
  • 인덱스는 추가적인 저장 공간을 사용합니다.

출처 : https://www.youtube.com/watch?v=ywYdEls88Sw&list=PLgXGHBqgT2TvpJ_p9L_yZKPifgdBOzdVH&index=99

 

728x90
반응형
728x90
반응형

[ 공부배경 ]

개발 공부를 시작하고, 내일배움캠프 끝자락쯤에 도커라는 단어를 들어봤다.

최종프로젝트로 바빠서 뭐하는 기술이지? 궁금해했지만

새로운 기술보다는 프로젝트 마무리가 더 급했다.

이후 프로젝트도 끝나고, 이력서 수백개씩 넣고 취업을 했고 1년이 지났다.

이제 이직준비를 하면서 많은 회사에서 도커와 쿠버네티스 경험을 필요로 한다는 것을 보고 궁금해졌다.

가볍게 유뷰트를 보고 정리해보려고 한다.

 

0. 요약

 

1. 서버와 컨테이너의 개념

  • 컨테이너는 일종의 서버로, 프로그램 소프트웨어가 실행되는 장소이다.
  • 서버는 단순히 하드웨어를 의미하며, 컴퓨터와 유사한 개념으로 이해하면 된다.
  • 모든 회사는 IT 서비스를 제공하기 위해 소프트웨어를 운영한다.
  • 개발자는 자신의 PC에서 소스 코드를 작성하고, 이를 테스트하기 위해 임시 소프트웨어를 실행한다.
  • 개발이 완료된 소프트웨어는 고객에게 서비스를 제공하기 위해 인터넷상 어디선가 실행되어야 한다.
  • 소프트웨어는 회사의 서버룸 또는 클라우드에서 실행될 수 있다.

 

2. 컴퓨터 메모리와 소프트웨어 가상화

  • PC에서 개발되었지만 온라인 서버도 사용 가능하다.
  • 소프트웨어는 일반적으로 2~4GB의 메모리를 필요로 하지만, 서버는 32GB 또는 64GB 메모리를 가지기도 한다.
  • 한 대의 서버에서 여러 개의 소프트웨어를 실행할 때 가상화 기술을 사용하여 효율적으로 운영할 수 있다.
  • 하나의 소프트웨어만 실행하는 경우 64GB 컴퓨터에서 4GB 소프트웨어를 실행하는 것은 비효율적이라고 한다.
  • 그러므로 기업은 가상화 기술을 활용하여 컴퓨터 한 대에서 여러 소프트웨어를 실행시켜 효율성을 높이는데 주력하고 있다.

 

3. 가상화 기술에 대한 이해: 가상머신과 컨테이너 가상화의 차이

  • 서버를 건물로 비유하면, 여러 사람이 사용하는 거주 공간을 분리할 수 있는 기술로 '가상화 기술'이 있다.
  • '가상머신' 방식은 오랫동안 사용됐고, 프로그램을 실행하는 방식으로 VM(가상머신)을 사용한다.
  • 반면 '컨테이너 가상화'는 가상 기술보다 가벼우며 빠르다. 컨테이너는 이사를 하는 것처럼 이동하며, 건물 안에 놓을 수 있다.
  • 가상화 기술에서 컨테이너를 사용하면, 기존 소프트웨어를 더 효율적으로 운영하고 더 많은 소프트웨어를 안전하게 실행할 수 있다.

 

4. 도커 컨테이너 생성 및 관리

  • 건물의 첫 주민이 도착하여 이사하려고 하는데, 도커를 활용한 컨테이너 생성이 필요하다.
  • 개인이 아닌 전문가의 도움(도커)으로 내부 구성물을 효율적으로 건설한다.
  • 도커를 통해 컨테이너를 손쉽게 관리하며, 집콕 전문가가 각 건물을 효율적으로 운영하도록 도와준다.

 

5. 도커와 쿠버네티스 관리

  • 도커를 운영하다 보면 지속적인 운영이 어려워질 수 있기 때문에 쿠버네티스를 고용한다. 쿠버네티스는 다수의 서버를 관리하기 위한 도구로, 오케스트레이션의 도구이다.
  • 쿠버네티스는 마치 다중 서버에서 지휘자처럼 각 서버에서 실행되며 도커에 지시를 내린다.
  • 쿠버네티스는 지능적인 직원으로 각 명령을 하나하나 내릴 필요 없이 컨테이너를 자동으로 관리한다.
  • 도커를 사용하면 서버에 안전하고 안정적인 소프트웨어 구동이 가능하며, 효율적인 컨테이너 관리를 위한 도구인 쿠버네티스를 배운다.

 

[출처] https://www.youtube.com/watch?v=eRfHp16qJq8

 

 

728x90
반응형

+ Recent posts