#ClickHouse 설치

 

 

ClickHouse는 오픈소스 DBMS로

일반적인 DBMS와 다르게 컬럼기반 DBMS이다.

 

컬럼기반 DBMS란?

데이터를 row 단위가 아닌 column 단위로 저장하고 관리하는 시스템을 말한다.

 

왜 기존의 DBMS를 변형한 컬럼기반 DBMS가 등장했을까?

데이터가 점점 많아지면서

 통계정보를 산출하고 자료를 분석을 하는데 시간이 많이 발생하게 되었다.

즉, 기존의 DBMS로 OLAP를 하는 경우 시간이 많이 발생하기 때문에

컬럼기반 DBMS가 등장하게 되었다.

 

컬럼기반 DBMS는 컬럼 단위로 저장이 되어있어서

동일한 컬럼 내에서 데이터를 갖고오는데 효율이 좋은 구조로

OLAP에 굉장히 잘 맞게 구성되어 있다.

 

ClickHouse는 컬럼기반 DBMS로 러시아 쪽에서 개발한 것으로 알고 있다.

이번 포스팅은 ClickHouse 설치하고

공식 문서에서 제공하는 샘플 데이터까지 설치하는 과정을 적으려 한다.

 

ClickHouse 설치

# yum install yum-utils
# rpm --import https://repo.yandex.ru/clickhouse/CLICKHOUSE-KEY.GPG
# yum-config-manager --add-repo https://repo.yandex.ru/clickhouse/rpm/stable/x86_64
# yum install clickhouse-server clickhouse-client
# /etc/init.d/clickhouse-server start
# clickhouse-client

설치는 매우 간단하다.

다음과 같이 ClickHouse 콘솔이 나오면 모두 설치가 된 것이다.

 

사용자 생성

# /etc/init.d/clickhouse-server stop
# vi /etc/clickhouse-server/user.xml

<user>
...

<사용자명>
    <password_sha256_hex> sha256 비밀번호 입력 </password_sha256_hex>
    <networks incl="networks" replace="replace">
            <ip>::1</ip>
            <ip>127.0.0.1</ip>
    </networks>
    <profile>default</profile>
    <quota>default</quota>
</사용자명>

</user>

 

서버를 잠시 멈추고

사용자 설정파일에서 사용자를 추가한다.

 

참고로 sha256 비밀번호를 생성하는 법은 다음을 참고한다.

# echo -n "비밀번호" | sha256sum | tr -d '-'
13a5c202e320d0bf9bb2c6e2c7cf380a6f7de5d392509fee260b809c893ff2f9

 

서버를 다시 시작하고 설정한 사용자로 클라이언트에 접속한다.

# /etc/init.d/clickhouse-server start
# clickhouse-client -h 127.0.0.1 -u 사용자이름 --password 비밀번호

 

SQL Editor - Tabix

ClickHouse 내부에는 SQL Editor인 Tabix가 있는데

Tabix로 ClickHouse를 실행해보자.

 

서버 설정파일에 Tabix 주석을 해제한 후 ip를 연다.

# vi /etc/clickhouse-server/config.xml
# 다음의 내용 주석 제거

<http_server_default_response><![CDATA[<html ng-app="SMI2"><head><base href="http://ui.tabix.io/"></head><body><div ui-view="" class="content-ui"></div><script src="http://loader.tabix.io/master.js"></script></body></html>]]></http_server_default_response>

<listen_host>::</listen_host>

 

서버를 재시작한 후

크롬 브라우저에서 http://ip주소:8123을 접속하면 Tabix 페이지가 나올 것이다.

사용자를 생성하지 않았다면 default로 하고 config.xml에 설정한 비밀번호를 입력한다.

 

ClickHouse 공식 홈페이지에 샘플 데이터가 있으므로

다운받아 테스트 해보는 것이 도움이 될 것이다.

 

만약 다른 DBMS와 비교를 하고 싶다면 클릭하우스 샘플 데이터를 마이그레이션해야 하는데

내 경험상 DBeaver로 쉽게 하려고 했지만 여러가지 문제로 데이터를 옮기지 못했다.

ClickHouse는 내부적으로 MySQL 엔진을 지원하고 있어서

해보진 않았지만 MySQL쪽으로 마이그레이션은 쉽지 않을까 싶다.

다른 DB로 해본다면

Tool을 사용하기보다 직접 컬럼을 수정해서 CSV나 JSON으로 하는 것이 더 빠를 것이라 생각한다.

 

클릭하우스 공식문서 - https://clickhouse.yandex/docs/en/

 

 

Made by 꿩

 

 

#Spring Boot와 JSP

 

 

우연히 Spring Boot를 https://start.spring.io/ 라는 사이트에서

프로젝트를 만들어 보았다.

 

평소같으면 기존 Spring Boot 프로젝트에서

틀이나 내용을 복붙해서 가져왔겠지만

 

새로운 스프링 부트 프로젝트를 만드는 연습을 한다고 생각하고

아무것도 복붙하지 않고 만들어 보았다.

 

그런데 왠걸...?

Hello World조차 화면에 나타내지 못했는데 5시간이 지났다.

아무리 설정파일을 고치고

기존 프로젝트를 참고해 똑같은 라이브러리를 깔고

톰캣을 8버전으로 낮춰도 보고

스프링 부트 버전을 여러번 바꿔도 보았지만

스프링 부트는

도무지 hello.jsp 파일을 찾지 못했다.

다른 점은 한가지 있긴 했다.

WEB-INF 폴더 밑이 아닌 templates 폴더 밑에 jsp 파일을 넣은 것이다.

 

templates 폴더 밑에 html파일은 읽히지만

jsp파일은 읽히지 않았다.

 

스프링 부트에서 jsp를 적용하려면

스프링 프로젝트처럼 webapp/WEB_INF/ 하위에 위치시키고

properties나 yml에서 경로를 설정해줘야 jsp파일을 사용할 수 있었다.

스프링 공식문서에 보면 내장된 서블릿 컨테이너에는 jsp 제한사항이 있다.

스프링 부트는 가능하다면 jsp를 피하고

Thymeleaf와 같은 템플릿 엔진을 사용하라고 권장한다.

국비교육에서 처음 스프링 부트를 배웠을 때

jsp로도 실습을 했었고

templates 폴더 안에 html 파일을 넣고 프로젝트까지 진행도 했었다.

 

원래는 강사님이 스프링 부트에서 jsp를 사용할 때

주의할 점이나 이유에 대해서 설명해 주셨었다.

그러나 시간이 지나면서

다 잊어버렸고

이제서야 희미하게 기억이 났다.

 

역시나

수업을 듣는다고 모든 내용을 흡수할 수 있는 것은 아니다.

이처럼

삽질도 하고 피부로 직접 느껴봐야

피가 되고 살이 되나보다.

스프링 공식문서 링크

https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-web-applications.html#boot-features-spring-mvc-template-engines

 

 

Made by 꿩

'IT > Java' 카테고리의 다른 글

추상클래스와 인터페이스  (0) 2018.11.11
다형성  (0) 2018.11.03
object vs instance vs class  (3) 2018.10.24

#[Spring] Google reCAPTCHA v3

 

 

오늘은 자동화 공격에 대한

구글이 제공하는 reCAPTCHA에 대해 소개하려 한다.

reCAPTCHA는 자동화 공격을 막기 위한 방법으로

흔히들 아는 "로봇이 아닙니다"를 떠올이면 된다.

자동화 공격은 사람이 아닌 기계로 공격을 하는 것으로

매크로를 만들어 불법적인 프로그램을 사용하거나

로그인을 할 때 비밀번호를 기계가 자동으로 입력하여

계정을 해킹하는 것 등을 말한다.

Google reCAPTCHA v2는

의심스러운 트래픽이 발생하면

'로봇이 아닙니다'를 증명하기 위해

사람만이 판단할 수 있는 이미지를 클릭하는 방식으로 방어하는 방식이다.

하지만 v2 방식은 이용자들에게 불편함을 주는 방식으로

이미지를 클릭해도 다른 이미지가 나와 여러번 클릭하거나

로그인할 때마다 로봇이 아니라는 것을 인증해야 해서

빡친 사람은 나뿐만이 아닐 것이다.

속사정은 모르지만

아마도 사용자를 방해한다는 피드백이 있어

reCAPTCHA v3가 나온 것이 아닐까 추측한다.

reCAPTCHA v3는 v2와 다르게 사용자를 방해하지 않는다.

사용자의 행동(action)을 보고 기계인지 사람인지 판단하는 방법이다.

reCAPTCHA v3는 0 ~ 1.0 까지의 점수를 서버에 보내주는데

0에 가까울수록 기계에 가깝다는 뜻이고

1에 가까울수록 사람에 가깝다고 알려준다.

간단하게 reCAPTCHA v3를 한번 구현해보자.

먼저, Google reCAPTCHA 사이트에 들어가 admin console 사이트를 등록한다.

https://www.google.com/recaptcha/intro/v3.html

 

v3 유형을 선택하고

도메인은 test용이니 localhost를 등록한다.

완료가 되면 사이트 키와 비밀 키를 받는데

설정에서 그 내용을 확인할 수 있다.

그 다음

Spring으로 간단한 maven 프로젝트를 생성한다.

recaptcha.jsp 파일은 다음과 같이 작성하였다.

<%@ page language="java" contentType="text/html; charset=utf-8" pageEncoding="utf-8"%>
<html>

<head>
    <meta charset="UTF-8">
    <title>구글 리캡챠 테스트</title>
    <script src="https://code.jquery.com/jquery-3.4.1.min.js" integrity="sha256-CSXorXvZcTkaix6Yvo6HppcZGetbYMGWSFlBw8HfCJo=" crossorigin="anonymous"></script>
    <script src="https://www.google.com/recaptcha/api.js?render=6Le-q6kUAAAAAFPDTU6lct7ZRRN7vK55hVF4Icp3"></script>
</head>

<body>
    <form action="/robot" method="get">
        <input type="text" name="name" />
        <input type="text" name="g-recaptcha-response" id="g-recaptcha-response" />
        <input type="submit" value="submit" />
    </form>

<script>
$(document).ready(function(){
    grecaptcha.ready(function() {
      grecaptcha.execute('reCAPTCHA_site_key', {action: 'login'}).then(function(token) {
         console.log(token)
         $.ajax({
            url: '${pageContext.servletContext.contextPath}/robot/token',
            type : 'POST',
            dataType: 'json',
            data : {'token': token},
            success : function(result){
                console.log(result);
            },
            fail: function(e){
                console.log("fail")
            }
          });// end ajax
      });
    });
});
</script>
</body>

</html>

Google DreCAPTCHA_site_key에 할당받은 사이트 키를 넣고

서버와 ajax 통신을 위해 jQuery를 이용했다.

일단 이 상태에서 토큰이 다음처럼 받아져야 한다.

이제 Controller에서 받아 Service 단에서 google reCAPTCHA와 통신을 한다.

통신 방법은 RestTemplate을 사용했으며

Post방식으로 토큰과 비밀키 값을 전송한다.

@Service
public class RecaptchaService {

    public RecaptchaDTO token(String token) {
        String url = "https://www.google.com/recaptcha/api/siteverify";

        RestTemplate restTemplate = new RestTemplate();

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
        map.add("secret", "secret-key");
        map.add("response", token);

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers);

        RecaptchaDTO response = restTemplate.postForObject( url, request, RecaptchaDTO.class );

        return response;
    }

}

받은 자료를 콘솔에 뿌려주면 다음처럼 나오게 될 것이다.

여기서 score를 보면 0.9라는 것을 볼 수 있다.

이는 reCAPTCHA가 사람에 가깝다는 것을 판단한 것이며

이 score를 이용하여 서비스의 동작을 통제하면 될 것이다.

참고자료

https://developers-kr.googleblog.com/2019/01/introducing-recaptcha-v3-new-way-to.html

https://dany-it.tistory.com/302

https://developers.google.com/recaptcha/docs/v3?hl=ko

 

 

Made by 꿩

'IT > 보안' 카테고리의 다른 글

[방화벽] 인바운드 & 아웃바운드  (0) 2022.01.31
OAuth  (0) 2019.06.17
SSL인증서  (0) 2019.03.21
CSRF  (0) 2018.12.25
XSS 공격과 방어  (2) 2018.10.25

#OAuth

 

 

'당신의 비밀번호는 안녕하십니까?'

 

 

이메일, 비밀번호 유출은 매우 빈번하다.

어디 조금만 가입해도 이상한 스팸메일이 날라오며

잊을만 하면

유명한 사이트에서 고객정보 유출 사건이 한번씩 터진다.

 

유명한 사이트도 개인정보 유출과 관련해서 사건사고가 많은데

잘 알지도 못하는 사이트에 개인정보를 입력하고 싶은가???

 

몇몇 사람들은 비슷하거나 똑같은 비밀번호를 사용하고

이메일도 주로 하나 또는 두 개만을 사용한다.

그런데 보안이 제대로 지켜지는 지도 모르는 사이트에

덜컥 회원가입을 하고 로그인 한다면

당신의 이메일과 비밀번호는 안전하겠는가???

 

 

이런 문제를 방지할 수 있는 것이

바로 OAuth이다.

 

위키피디아에 따르면

OAuth는 인터넷 사용자들이 비밀번호를 제공하지 않고

다른 웹사이트 상의 자신들의 정보에 대해 웹사이트나

애플리케이션의 접근 권한을 부여할 수 있는 공통적인 수단으로서 사용되는

접근 위임을 위한 개방형 표준이다.

 

내가 생각하는 OAuth란 쉽게 말해서

인증과 허가를 대신해주는 것이라 생각한다.

 

 

예를 들어,

쇼핑몰 홈페이지가 있다고 가정해보자.

이 쇼핑몰에서 옷을 구매하여 포인트를 적립하고 싶지만

로그인을 해야한다.

게다가 기억력이 안 좋아서

항상 동일한 비밀번호를 사용하기만 한다.

그렇다고 잘 알지도 모르는 사이트에

내 비밀번호를 입력하는 것도 망설여진다.

 

이때 소셜 로그인을 하면

굳이 잘 알지도 모르는 사이트에

비밀번호 입력을 하지 않아도 되고

로그인이 간편해진다.

 

OAuth를 이용한 인증방식에 대해 알아보자.

1. 고객이 카카오 로그인을 선택하면 쇼핑몰 서버는 카카오 로그인 페이지를 전송해준다.

2. 고객은 ID와 비밀번호를 입력해 본인을 인증한다.

3. 카카오 서버는 고객을 인증완료 페이지로 redirect 시킨다.

4. 카카오 서버는 redirect하면서 authorize_code 값을 포함시키고 쇼핑몰 서버는 이것을 수집한다.

5. 쇼핑몰 서버는 authorize_code로 카카오 서버에 access token을 요청한다.

6. 카카오 서버는 쇼핑몰 서버에 access token을 발급해준다.

7. 쇼핑몰 서버는 카카오 API 서버에 access token으로 사용자 정보를 불러온다.

 

정리하자면,

사용자는 카카오에서 로그인을 함으로써 본인을 인증하고

카카오는 인증을 확인하고 사용자 정보 접근을 허가하는 token 값을 발급한다.

token 값으로 쇼핑몰 서버는 사용자 정보를 카카오에서 갖고 오게 된다.

쉽게 말해 쇼핑몰과 사용자 사이에 카카오라는 중간자 역할을 둔 것이다.

 

 

참고자료

https://ko.wikipedia.org/wiki/OAuth

http://tech.devgear.co.kr/delphi_news/449506

https://d2.naver.com/helloworld/24942

 

 

To be continued.........

 

 

Made by 꿩

'IT > 보안' 카테고리의 다른 글

[방화벽] 인바운드 & 아웃바운드  (0) 2022.01.31
[Spring] Google reCAPTCHA v3  (6) 2019.07.07
SSL인증서  (0) 2019.03.21
CSRF  (0) 2018.12.25
XSS 공격과 방어  (2) 2018.10.25

#Chain of Responsibility

 

 

1. 개념

 

Chain of Responsibility(책임 사슬)패턴

어떤 하나의 문제가 발생했을 경우

그 문제의 처리를 담당하는 여러개의 처리기를 두고

순서대로 처리해 나가는 패턴을 말한다.

 

자바에서 바로 try/catch/finally

책임 사슬 패턴을 적용한 예이다.

try{}블럭 안에서 예외가 발생했을 경우

catch{}블럭으로 이동하는데

어떤 예외인지에 따라 어떤 catch{}블럭이 실행되는지 결정되는 행위가

역할 사슬 패턴이라는 것이다.

 

 

2. Why? 왜 사용해야 할까?

 

프로젝트를 수행하다보면 무수히 많은 객체들을 생성하게 된다.

자신이 만든 모든 객체의 위치와 내용을 다시 기억해 내기란

실질적으로 힘든 경우가 있다.

이처럼

어떤 요청에 대해 처리해주는 객체의 위치를 파악하기 어려운 상황에 대비하여

이러한 요청을 처리해주는 객체들의 집합체나 계층화가 되어있으면

사용자는 굳이 객체의 위치를 알 필요없이

손쉽게 요청을 처리할 수 있다.

 

 

3. 장단점

 

책임 사슬 패턴을 사용하면 모든 후보 객체들을

다 알 필요없이 단순하게

자신과 연결된 단 하나의 후보 객체만 알면된다.

이처럼 객체들 간의 상호작용 과정을 단순화시키기에

객체 간의 결합도가 낮아지는 방법이 된다.

 

객체의 책임을 여러 객체에게 분산시키는 과정에서

상속을 이용하므로

객체의 기능을 추가, 삭제 및 수정하기 용이하다.

 

그러나 이 방식은 요청이 처리된다는 보장이 없다.

객체들 간의 연결고리가 잘 정의되어 있어야 하며

객체들 중 아무도 요청을 처리하지 못했을 경우

요청은 그냥 버려질 수 있다는 가능성이 있다.

이때 Exception을 던지는 방식 등으로 대처를 할 수 있다.

 

 

4. 클래스 다이어그램

 

5. 예제 코드

 

코드 예제는 java에서 Exception에서 아이디어가 떠올랐다.

실제 구동되는 동작은 다르지만 이해가 수월하길 바란다.

 

Exception 객체들은 모두 Problem 객체에 의해 관리되고 있으며

하위 클래스에서 자신이 처리할 수 있는 에러인지 확인하고

객체를 생성하는 코드를 구현해보았다.

public abstract class Problem {
	
	private static Problem[] list = {new SocketException(), new NullPointException(), new IOException()};
	protected static Integer num = 0;
	protected static String msg;
	
	public static Problem next(String error) {
		msg = error;
		
		if(num == list.length) {
			System.out.println("어떤 에러인지 확인 불가능");
			return null;
		}
		
		return list[num++].cause();
	}

	
	public abstract Problem cause();
}

 

하위 클래스 Exception객체들은 다음과 같이 구현되었다.

public class SocketException extends Problem{

	@Override
	public Problem cause() {
		if("socket".equals(msg)) {
			System.out.println("소켓 문제 발생! SocketException 객체 생성");
			num = 0;
			return this;
		}
		
		//다음 객체 ㄱㄱ
		return next(msg);
	}
}

public class NullPointException extends Problem{

	@Override
	public Problem cause() {
		
		if("null".equals(msg)) {
			System.out.println("객체에 Null 값이 들어감. NullPointException 객체 생성");
			num = 0;
			return this;
		}
		
		//다음 객체 ㄱㄱ
		return next(msg);
		
	}
}

public class IOException extends Problem{

	@Override
	public Problem cause() {
		
		if("IO".equals(msg)) {
			System.out.println("입력 값 오류 발생! IOException 객체 생성");
			num = 0;
			return this;
		}
		
		//다음 객체 ㄱㄱ
		return next(msg);
	}

}

 

클라이언트에서 에러가 발생해 Problem 객체를 호출하고

문제의 원인을 물어보는 코드를 구현했다.

public class Client {

	public static void main(String[] args) {
		
		System.out.println("에러 발생!");
		System.out.println("");
		
		
		//입력 오류로 에러가 발생한 경우
		System.out.println("입력 오류가 발생한 경우");
		Problem problem1 = Problem.next("IO");
		System.out.println("");
		
		//객체에 null값으로 에러가 발생한 경우
		System.out.println("null 값으로 에러가 난 경우");
		Problem problem2 = Problem.next("null");
			

	}

}

 

정리하자면 Client에서 오류가 발생하였고

Problem 객체를 이용하여 오류에 해당하는 객체를 생성하는 과정의 코드이다.

결과는 다음과 같이 나온다.

 

6. 참고

 

https://leetaehoon.tistory.com/64

https://ehclub.co.kr/2210

https://blog.naver.com/2feelus/220655715030

http://egloos.zum.com/iilii/v/3863886

GoF의 디자인 패턴

 

 

Made by 꿩

 

 

'스터디 > GoF의 디자인패턴' 카테고리의 다른 글

Strategy Pattern  (0) 2019.04.30
Template Method Pattern  (0) 2019.04.30
Decorator Pattern  (0) 2019.04.28
Factory Method Pattern  (0) 2019.04.23

#Strategy Pattern

 

 

1. 개념

 

전략 패턴(Strategy Pattern)이란 알고리즘 대체를 위해

동일한 목적을 지닌 알고리즘군을 인터페이스로 묶고

캡슐화하여 서로 대체가 가능하게 사용하는 것을 말한다.

 

예를 들어

종로에서 충무로를 가는 방법을 생각해보자

시간이 들더라도 걸어갈 수 있으며

택시를 타도 되고

버스나 지하철을 탈 수도 있다.

각각의 시간과 비용은 모두 다르지만

한 곳을 간다는 목적은 동일하다.

 

이처럼 비슷한 목적을 지닌 알고리즘을 묶어서

서로 상호교환이 가능하도록 만드는 패턴을

전략 패턴이라 한다.

 

 

2. Why? 왜 사용해야 할까?

 

구현된 알고리즘은 다르지만 동일한 목적을 지닌 클래스들이 존재할 때

전략 패턴을 사용하는 것이 좋다.

한 가지 알고리즘을 평소에 사용하더라도

참조하는 클래스의 변경이나 제거 등 여러가지 상황이 발생하거나

사용하는 시점에 따라서 알고리즘을 변경해야 하는 상황이 있을 수 있다.

이런 경우 언제든지 알고리즘을 쉽게 대체할 필요성이 있으며

전략 패턴을 사용하게 된다면

새로운 알고리즘을 추가하는 확장이 용이하기 때문이다.

 

 

3. 장단점

 

전략 패턴은 알고리즘의 변경 부분만을 따로 빼내어 구현한 것이므로

알고리즘마다 사용되는 코드 중복을 방지할 수 있으며

알고리즘을 쉽게 대체할 수 있고

확장성이 용이하여 다양한 알고리즘을 쉽게 추가할 수도 있다.

클라이언트와 독립적이기 때문에 알고리즘 변경에 대해서도 강하다.

 

그러나

알고리즘이 늘어날수록 객체도 무한정 늘어나며

클라이언트가 사용할 객체를 직접 결정해야 하기 때문에

많은 알고리즘에 대한 성능과 효율을 알고 있어야 하고

서로간의 차이를 알아야 하는데

직접 만든 것이 아니라면 부담스러울 수 있다.

 

 

4. 추가내용

 

앞서 템플릿 메소드 패턴에서 언급했듯이

보통 전략 패턴은 템플릿 메소드 패턴과 비교가 된다.

전략 패턴은 템플릿 메소드 패턴과 구성은 동일하지만 사용목적이 다르다고 볼 수 있다.

템플릿 메소드 패턴 공통된 메소드를 통해서 코드의 중복없이 사용하는 것이고

전략 패턴 상황에 맞는 필요한 전략을 선택하기 위해 사용하는 패턴이다.

 

그리고

객체의 행위를 유연하게 확장하고 구조가 데코레이터 패턴과 유사하지만

전략 패턴코드 내부의 알고리즘을 변화시키고

데코레이터 패턴코드 외부의 리턴 값을 변화시킨다는 점에서 차이가 있다.

 

 

5. 클래스 다이어그램

 

 

6. 예제 코드

 

포켓몬을 잡는 코드를 보며 전략 패턴에 대해서 알아보자

당신은 포켓몬 게임을 시작하며 스타팅 포켓몬으로 치코리타를 받았다.

당신에겐 절실히 다른 포켓몬이 필요하여 잠만보를 잡으려 한다.

당신에겐 몬스터볼, 하이퍼볼, 마스터볼이 있다.

세 개의 몬스터볼을 전략 1,2,3으로 구현하였고 몬스터볼 인터페이스로 묶어봤다.

public interface MonsterBall {
	
	public int throwBall();

}

public class Strategy1 implements MonsterBall{

	@Override
	public int throwBall() {
		System.out.println("몬스터 볼을 던진다.(확률:20%, 가격: 천원)");
		return 1;
	}
}

public class Strategy2 implements MonsterBall{

	@Override
	public int throwBall() {
		System.out.println("하이퍼볼을 던진다.(확률:50%, 가격: 삼천원)");
		return 4;
	}
}

public class Strategy3 implements MonsterBall{

	@Override
	public int throwBall() {
		System.out.println("마스터볼을 던진다.(확률:100%, 가격: 백만원)");
		return 9;
	}
}

 

Player1 캐릭터는 포켓몬을 잡기 위해 몬스터볼을 던질 수 있게 만들었고

전략을 파라미터로 받는다.

public class Player1 {
	
	public int getPokemon(MonsterBall monsterBall) {
		
		return monsterBall.throwBall();
		
	}
}

 

당신은 나중을 위해 하이퍼볼과 마스터볼을 아껴놓기로 하고

일반 몬스터볼(전략1)을 던져보기로 결정했다.

import java.util.Random;

public class PokemonGame {

	public static void main(String[] args) {
		
		Player1 player1 = new Player1();
		
		System.out.println("야생의 잠만보가 출현했습니다.");
		Random random = new Random();
		
		int ran = random.nextInt(10);
		int prob = player1.getPokemon(new Strategy1());
		
		if(ran <= prob) {
			System.out.println("잠만보를 잡았습니다!");
		} else {
			System.out.println("아쉽게 놓쳤다, 잠만보가 도망쳤다.");
		}

	}
}

 

결과는 다음과 같다.

 

 

7. 참고

 

https://rearrangingall.blogspot.com/2017/01/01-strategy-pattern.html

https://niceman.tistory.com/133

https://kimsunzun.tistory.com/entry/Strategy%EC%A0%84%EB%9E%B5-%ED%8C%A8%ED%84%B4

GoF의 디자인 패턴

 

 

Made by 꿩

'스터디 > GoF의 디자인패턴' 카테고리의 다른 글

Chain of Responsibility  (0) 2019.05.06
Template Method Pattern  (0) 2019.04.30
Decorator Pattern  (0) 2019.04.28
Factory Method Pattern  (0) 2019.04.23

#Template Method Pattern

 

 

1. 개념

 

탬플릿 메소드 패턴여러 클래스에서 공통된 행위는 상위 클래스에서 정의하고

하위 클래스에서 각각의 상세부분을 구현하는 것을 말한다. 

 

템플릿이란 큰 구조를 정의한 틀로서

전체적인 틀은 동일하되 상세적인 부분은 다르게 찍어낼 수 있는 것을 말한다.

블로그를 꾸며본 사람은 알겠지만

대부분 블로그들은 여러 디자인의 템플릿을 제공해주고

일정한 틀 안에서 자신만의 블로그를 쉽게 꾸밀 수 있도록 도와준다.

 

이러한 개념을 객체에 적용한다면

여러 객체들은 추상클래스 또는 인터페이스를 통해서

동일한 이름의 메소드를 구현하되

기타 메소드는 각 객체에 맞게 구현하는 것이다.

 

 

2. Why? 왜 사용해야 할까?

 

객체를 생성하다 보면 비슷한 동작을 하는 객체들이 많다.

비슷한 동작을 하는 객체들에서 변하지 않는 부분을 한 번 정의해 놓고

다양해질 수 있는 부분은 하위 클래스에 정의할 수 있는 경우

템플릿 메소드 패턴을 이용하면

코드의 중복을 피할 수 있고

상위 클래스로 묶여있기 때문에

if - else 문을 쓰지 않고 다양한 객체를 생성할 수 있다.

 

 

3. 장단점

 

템플릿 메소드의 장점은 쉬운 확장성에 있다.

일정한 틀이 있기 때문에

추상 클래스를 상속받아

쉽게 하위 클래스를 생성, 추가해 나갈 수 있다.

 

또한 앞서 언급했듯이

로직을 공통화하게 되면

비슷한 알고리즘은 일일히 기술할 필요가 없어져

코드의 중복을 줄일 수 있다.

 

그러나 템플릿 메소드는 조심해야한다.

상위 클래스가 관리하는 하위 클래스가 많을 때

상위 클래스를 수정하게 될 때

모든 하위 클래스들을 일일히 수정해야 하는 상황이 발생한다.

 

 

4. 추가내용

 

템플릿 메소드처럼 상속을 통해 확장하는 패턴이 팩토리 메소드 패턴이다.

두 패턴은 비슷하지만 명확한 차이가 있다.

팩토리 메소드 패턴객체의 생성을 리턴하는 메소드를 만드는 것이고

템플릿 메소드 패턴객체의 행위를 동작하는 공통된 메소드를 만드는 것이다.

 

템플릿 메소드 패턴과 비교할 때 빠지지 않는 패턴은 바로 전략 패턴이다.

전략 패턴은 템플릿 메소드 패턴과 구성은 동일하지만 사용관점이 틀리다고 볼 수 있다.

템플릿 메소드 패턴객체의 행위를 동작하는 공통된 메소드를 만드는 것이고

전략 패턴은 동일 이름의 메소드 중 상황에 맞는 필요한 전략을 사용하는 패턴이다.

 

 

5. 클래스 다이어그램

6. 예제 코드

 

탑 라인에서 티모와 블라디가 맞붙는다.

서로의 스킬과 소환사의 주문을 출력하는 코드를 통해

템플릿 메소드 패턴을 알아보자.

 

먼저 탑라이너라는 추상클래스에는 점멸과 순간이동 메소드가 구현되어 있고

스킬과 궁극기 스킬은 추상 메소드로 명시되어 있다.

public abstract class TopLiner {
	
	public void flashing() {
		System.out.println("점멸!");
	}
	
	public void warp() {
		System.out.println("순간이동!");
	}
	
	public abstract void skill();
	
	public abstract void ultimateSkill();
	
}

 

티모와 블라디는 추상클래스를 상속받아 각자의 스킬을 구현한다.

public class Teemo extends TopLiner{

	@Override
	public void skill() {
		System.out.println("실명!");
		
	}

	@Override
	public void ultimateSkill() {
		System.out.println("독버섯!");
	}

}

public class Vladimir extends TopLiner{

	@Override
	public void skill() {
		System.out.println("수혈!");
		
	}

	@Override
	public void ultimateSkill() {
		System.out.println("혈사병!");
		
	}

}

 

메인 클래스에서 각자의 스킬 및 소환사 주문을 확인하면

다음의 결과가 나온다.

public class SummorsGorge {

	public static void main(String[] args) {
		
		System.out.println("소환사의 협곡에 오신 것을 환영합니다.\n");
		
		TopLiner top1 = new Teemo();
		TopLiner top2 = new Vladimir();
		
		System.out.println("티모 스킬");
		top1.skill();
		top1.ultimateSkill();
		System.out.println("소환사 주문");
		top1.flashing();
		top1.warp();
		
		System.out.println("");
		
		System.out.println("블라디 스킬");
		top2.skill();
		top2.ultimateSkill();
		System.out.println("소환사 주문");
		top2.flashing();
		top2.warp();
		
	}

}

 

 

7. 참고

 

https://leetaehoon.tistory.com/47

https://dlucky.tistory.com/145

https://kimddochi.tistory.com/55

https://hyeonstorage.tistory.com/146

https://rearrangingall.blogspot.com/2017/01/01-strategy-pattern.html

GoF의 디자인 패턴

 

 

Made by 꿩

'스터디 > GoF의 디자인패턴' 카테고리의 다른 글

Chain of Responsibility  (0) 2019.05.06
Strategy Pattern  (0) 2019.04.30
Decorator Pattern  (0) 2019.04.28
Factory Method Pattern  (0) 2019.04.23

#Decorator Pattern

 

 

1. 개념

 

데코레이터 패턴(Decorator pattern)은 객체의 추가적인 요건을 동적으로 추가하는 패턴이다.

Decorator는 글자 그대로 장식가로서, 중심이 되는 객체가 반환하는 값에

추가적으로 더해져서 결과값을 반환합니다. 

 

쉽게 스타벅스를 생각해보자

스타벅스는 자신만의 커스터마이징 음료를 만들 수 있다.

나같은 경우는 그린 티 크림 프라푸치노(Green Tea Cream Frappuccino)를 주문해서

에스프레소 샷 추가, 저지방 우유, 라이트 시럽, 휘핑크림, 자바칩 갈아 만든

커스터마이징 음료를 산다.

 

이와 같이 하나의 객체에 옵션들을 여러개 추가해서

만드는 패턴을 장식하는 것과 같다하여

데코레이터 패턴이라 한다.

 

 

 

(출처: http://i.stack.imgur.com/O7pqc.png )

 

2. Why? 왜 사용해야 할까?

 

하나의 객체에 부가적인 기능을 덧붙여야 할 때가 있다.

또한 동일한 객체를 여러개 만들어야 하지만

각기 다른 기능을 첨가할 필요가 있을 때

데코레이터 패턴을 이용하면

많은 객체에게 다양한 부가기능을 쉽고 빠르게 적용할 수 있다.

 

 

 

 

3. 장단점

 

데코레이터 패턴의 핵심은

중심이 되는 객체를 놔두고 추가적인 사항을 첨가하는 방식이라

객체에 기능을 추가 또는 삭제 할 때

 중심이 되는 객체를 수정하지 않고 동적으로 추가 또는 삭제할 수 있다.

객체지향의 원칙 중 OCP에 충실한 패턴인데

OCP는 Open-Close Principle로서 확장에는 열려있고 변경에는 닫혀 있어야 한다는 원칙이다.

 

데코레이터 패턴을 사용하게 되면

기존 객체는 변경하지 않고 자유로운 확장을 구현할 수 있으며

기존 객체가 수정되더라도 또는 데코레이터가 수정되더라도

해당 클래스의 내용만 수정해주면 되기 때문에 변경에 대해서도 닫혀있다.

 

또한,

기본적인 데이터에 첨가하는 데이터가 일정하지 않고 다양할 때

데코레이터 패턴을 사용하면 효율적으로 사용할 수 있다.

 

반면에

데코레이터를 너무 많이 사용하게 되면

자질한 객체가 많이 추가될 수 있고

오히려 코드가 복잡해 질 수 있다는 단점이 있다.

 

 

4. 추가내용

 

비슷하게 확장을 하는 상속과 데코레이터 패턴의 차이는 무엇일까?

상속은 하위 클래스를 생성하는 방법으로 확장을 하며

데코레이터 패턴은 기본 객체에 추가하는 객체를 생성하는 방법으로 확장을 한다.

상속은 오버라이딩을 통해 기능을 전부 명시해 줘야 하지만

데코레이터 패턴은 기본 기능은 놔두고 추가되는 기능만 명시해 줄 수 있다는 차이가 있다.

 

데코레이터 패턴은 여러 패턴들과 유사한 구조를 가지고 있다.

 

다양하고 일정하지 않은 여러 객체들을 하나의 객체로 묶는 것Composite 패턴이라 하는데

데코레이터 패턴은 한 구성요소 만을 갖는 Composite 패턴이라 볼 수 있다.

그러나 목적에서 차이가 있다.

Composite 패턴은 목적이 생성되어 있는 객체들 간의 합성에 있고

데코레이터 패턴은 목적이 객체에 새로운 행동을 추가하는 데에 있다.

 

Strategy 패턴유사한 행위를 캡슐화하는 인터페이스를 정의하여

객체들의 행위를 유연하게 확장하는 방법으로 데코레이터 패턴과 구조가 비슷하다.

하지만 Strategy 패턴은 객체의 내부를 변화시키며

데코레이터 패턴은 새로운 객체를 추가하여 객체를 변경한다는 점에서 차이가 있다.

 

마지막으로 Adaptor 패턴서로 다른 인터페이스를 통일시켜 사용하는 패턴으로서

데코레이터가 기준 객체와 부가 객체를 연결하는 어댑터 역할을 하는 것과 유사하다.

그러나 데코레이터 패턴은 어댑터 패턴과 달리 인터페이스를 변경 없이

객체에 새로운 행동을 추가한다는 점에서 차이가 있다.

 

 

5. 클래스 다이어그램

 

 

6. 예제 코드

 

스타벅스에서 그린티 크림 프라푸치노에

샷추가, 저지방 우유, 자바칩을 추가했을 때 가격을 알아본다.

 

public interface StarbucksMenu {
	
	public int cost();

}

public class GreenTeaCreamFrappuccino implements StarbucksMenu{
	
	public GreenTeaCreamFrappuccino() {
		System.out.println("그린티 크림 프라푸치노를 선택하셨습니다.");
	}

	@Override
	public int cost() {
		System.out.println("그린티 크림 프라푸치노 가격 : 5000");
		return 5000;
	}

}

public abstract class CoffeeDecorator implements StarbucksMenu{

	public abstract int cost();
	
}

스타벅스 메뉴 인터페이스에

기본메뉴인 그린티 크림 프라푸치노와

부가메뉴인 커피데코레이터가 연결되어있다.

 

public class EspressoShot extends CoffeeDecorator{
	
	StarbucksMenu starbucksMenu;

	public EspressoShot(StarbucksMenu starbucksMenu) {
		System.out.println("샷 추가 + 1000원 추가");
		this.starbucksMenu = starbucksMenu;
	}

	@Override
	public int cost() {
		return starbucksMenu.cost() + 1000;
		
	}
}

public class JavaChip extends CoffeeDecorator{
	
	StarbucksMenu starbucksMenu;

	public JavaChip(StarbucksMenu starbucksMenu) {
		System.out.println("자바 칩 +500원 추가");
		this.starbucksMenu = starbucksMenu;
	}

	@Override
	public int cost() {
		return starbucksMenu.cost() + 500;
	}
}

public class LowFatMilk extends CoffeeDecorator{
	
	StarbucksMenu starbucksMenu;

	public LowFatMilk(StarbucksMenu starbucksMenu) {
		System.out.println("저지방 우유 +300원 추가");
		this.starbucksMenu = starbucksMenu;
	}

	@Override
	public int cost() {
		return starbucksMenu.cost() + 300;
	}
}

추상클래스인 커피데코레이터의 구현은

자식클래스인 자바칩, 저지방우유, 에스프레소샷에 구현된다.

 

public class Customer {

	public static void main(String[] args) {
		System.out.println("그린티 프라푸치노에 자바칩, 저지방우유, 샷 추가 해주세요.");
		
		StarbucksMenu order = new EspressoShot(new LowFatMilk(new JavaChip(new GreenTeaCreamFrappuccino())));
		
		System.out.println("총 가격은 " + order.cost() + "입니다.");
	}
}

고객이 위와 같이 주문을 하면

기본 메뉴를 먼저 생성한 후

기본 메뉴에 추가메뉴를 더해 총 가격을 구할 수 있다.

결과 창은 다음과 같다.

 

7. 참고

https://lalwr.blogspot.com/2016/02/decorator-pattern.html

https://secretroute.tistory.com/entry/Head-First-Design-Patterns-%EC%A0%9C3%EA%B0%95-Decorator-%ED%8C%A8%ED%84%B4

https://meaownworld.tistory.com/91

https://meylady.tistory.com/53

https://free-strings.blogspot.com/2016/04/adapter-decorator-facade-proxy.html

https://jusungpark.tistory.com/9

 

Made by 꿩

'스터디 > GoF의 디자인패턴' 카테고리의 다른 글

Chain of Responsibility  (0) 2019.05.06
Strategy Pattern  (0) 2019.04.30
Template Method Pattern  (0) 2019.04.30
Factory Method Pattern  (0) 2019.04.23

+ Recent posts