Web Study.docx


(파일참고)

개념 정리(Javascript, react, flux, redux)

 

JavaScript, Node.js 의 개념

l  JavaScript : Web 에서만 동작할 수 있었던 언어이다. Web browser 에서 동작하는 프로그램을 개발하기 위한 언어였다. 구글의 GMAIL, GMAP을 시작으로 Javascript로 고성능 Web application을 만들 수 있음을 깨닫게 된다.

-       구글은 Chrome 이라는 브라우저를 만들면서, 브라우저의 성능을 높이기 위해서 자바스크립트 엔진을 직접 개발 – V8 (오픈소스로 개방함)

-       V8엔진을 그대로 가져다가 내가 만드는 시스템에서 활용하면 Javascript를 웹이 아닌 다른 곳에서 빠른 고성능으로 Application을 만들 수 있게 됨.

-       위와 같은 이유로 자바스크립트로는 웹에 국한되지 않고, ‘탈웹화하기 시작한다.

 

l  Node.js : 서버 쪽에서 동작하는 Web Application을 만들 수 있도록 해준다.

n  구글의 V8 자바스크립트 엔진을 바탕으로 한 개발 프레임워크 이다.

n  자바스크립트로 Node.js 코드를 작성하고 나면, 이 코드가 실행될 수 있게 V8이 기계어로 컴파일 한다.

-       V8 자바스크립트 엔진 + event-driven programming + non-blocking IO = Node.js

-       웹 브라우저가 아닌 서버 쪽에서 자바스크립트로 개발할 수 있도록 해준다.

n  장점 :

u  Javascript end-to-end : 클라이언트 개발자와 서버 개발자가 동일한 언어를 사용하게 된다.

u  Event-driven : Node.js는 웹 요청을 처리하는데 특유의 로직을 사용한다.
웹 요청 처리를 위해 여러 스레드가 기다리도록 만드는 대신, 기본 이벤트 모델을 이용해서 웹 요청을 동일한 스레드에서 처리한다. 기존 웹 서버들보다 효율적인 방식으로 동작할 수 있도록 한다.

u  Extensibility : 새로운 기능의 추가가 적극적이고 빠르다. (개발자들이 각자 더 좋은 기능을 검증하고 추가함.. openSource… )

u  Fast Implementation : 설치가 쉽고 금방 웹 서버가 동작하도록 만들 수 있다.

 

l  Web 에서 동작하는 JavaScript  VS  Node.js 에서 동작하는 JavaScript ??

n  위에서 JavaScript는 한국어,  Web, node.js는 병원, 법원으로 구분. 병원 법원에서 쓰는 용어 등은 서로 다름. 그러나 문법이나 언어 자체는 한국어를 씀.. 즉 같은 언어를 쓰지만, 병원 법원에서 쓰이는 용어(함수, API 이름 등등)를 알아야, 활용할 수 있다.!

l  JavaScript의 의미는 아래의 두 가지 의미로 보통 사용된다.

n  Language : JavaScript (한국어)

n  Runtime (자바스크립트가 동작하는 환경) : Web Brower, Nodejs (병원, 법원..)

l  Web browser(Client) , Web Server : 양 쪽 모두 JavaScript를 써서 개발이 가능해진다.
즉 하나의 언어로 하나의 Application을 개발 할 수 있다. (원래 Server 쪽은 흔히 Ruby, Java, PHP 등의 웹 서버 프로그래밍을 위해 쓰이는 언어들이 있었다.)
위와 같은 이유로 우리는 JavaScript NodeJs 기술을 배운다.

l  나만의 이해하기 쉬운 예) – 삭제예정..틀린 예인 듯.. _..

상황) 한국어로 말을 하면 일련의 과정을 거쳐 대형 화면에 시각적으로 보이는 시스템이 있다고 하자. ‘언어 -> 번역기 -> 화면에 출력이러한 과정을 거친다.

번역기는 한국어를 화면에 출력해줄 수 있도록 번역해준다.

 

 번역기는 Web Browser가 있고, Node.Js가 있다. Web Browser에서만 쓰이던 V8 엔진을 Node.js도 가지게 되면서, Server에서도 javaScript를 사용할 수 있다.

 결국 Client가 아닌 Server에서도 Node.js 환경에서 javascript로 구현을 하면, 내부적으로 V8엔진을 통해 해당 javaScript 내용을 빠르고 효과적으로 수행하여, 결과를 Browser에게 전달 할 수 있다.

 

 

 

 

모듈과 NPM

 

 Application 에서 부품으로 사용할 로직인 모듈에 대해서 알아보고 모듈을 편리하게 관리하는 기술인 NPM을 사용하는 기본적인 방법 정리.

 

Module

l  Node.js에서 Server를 개발하기 쉽도록 API를 제공해주고 있다.

n  Ex) const http = require(‘http’); require를 함수로 http 모듈을 불러오는 코드이다.

l  Module을 불러다 쓰고 싶으면 ‘require’ 키워드를 활용한다.

l  기본적인 Module Node.js 에서 기본적으로 제공한다.

l  https://nodejs.org/dist/latest-v6.x/docs/api/

NPM : Node Package Manager (Node 세계의 앱 스토어)

l  Module의 설치 / 삭제 / 업그레이드 / 의존성 관리

l  다른 사람이 만든 Module을 우리 프로젝트에 가져와서 사용하는 방법을 살펴보자.

l  좋은 Module을 잘 활용하는 것도 중요하다.

l  Npmjs.com 에 가면, 다른 사람들이 등록해 놓은 Module들을 다운로드 받을 수 있고 그 Module에 대한 Docs 를 볼 수 있다.

l  NPM에 올라와 있는 Module들은 크게 두 가지 유형으로 존재하며, 하나는 독립적으로 실행 될 수  있는 프로그램, 또 다른 하나는 다른 프로그램에 부품처럼 들어가서 사용 되는 형태이며, 다운로드 받을 때, -g 옵션으로 구분하여 받을 수 있다. –g Global 약자로 독립적으로 실행 가능한 프로그램을 받고자 할 때 –g를 붙여서 npm install 한다.

-       Npm install module_name –g : 전역적으로 사용가능한 즉 독립적으로 실행될 수 있는 형태로 module을 받겠다.

-       Npm install module_name : 다른 패키지에 포함되어서 사용될 부품 형태로 다운로드 받겠다.

-       Npm install module_name –save : 패키지에 dependencies에 추가하여, pacakage.json 으로 관리하겠다.

-       Npm init : Npm install 을 하기위해서는 현재 프로젝트 폴더를 npm의 패키지로 초기화를 시켜야 한다. 이때 쓰기 위한 명령어이다.

 

l  Ex) uglify module을 통해서, 소스코드를 위한 가독성 코드 등을 삭제한 필수 코드만 남기게 된다네트워크에서는 보내야 하는 데이터를 최소화 해야 한다. (다 비용이며, 공백도 실제로는 다 데이터이다…_) 보통 uglify minify 와 같은 의미로 보통 코드를 최소화 한 경우, Pretty.min.js 로 저장해준다. (관습적으로 이름을 min 을 붙여 만들어 준다. 의미는 pretty 파일을 최소화 한 js 파일이다라는 의미이다.)

l  NPM 사이트에서 하나의 Module을 부품으로 우리 프로젝트에서 사용하기 위해서 underscore 라는 이름의 Module을 사용하고자 한다고 하자.
npm install
을 먼저 진행하기 전에, npm 에서 받는 module 처럼 우리 프로젝트도 npm에 패키지로 등록을 해줘야 한다. 이때의 작업을 하기 위해선
npm init 
이라는 명령어를 우리 프로젝트 폴더에서 해줘야 한다.

n  : npm init
Name, version , .. , entry point : …., git repository (
우리의 프로젝트를 git 에 올려놓을거면 git repository 써라.. 안써도 된다.)
다 입력하고 나면, 우리 프로젝트 내에 package.json 파일이 생선된다.
 
위와 같은 작업으로 우리의 프로젝트는 npm의 패키지로 등록이 되었다.
 
추후에 우리의 프로젝트를 npm 에 업로드 하여, 쉽게 설치할 수 있도록 초석을 닦아 두었다.
 
여기까지 하면, 다른 사람의 module을 가져다 쓸 수 있는 준비가 완료되었다.

n  이제 npm install module_name 을 입력하면, module 받아지고, 우리의 프로젝트에 자동으로 추가가 된다. 프로젝트에 가보면 node_modules 폴더가 새롭게 생성되고 그 아래 module name의 폴더가 하나 추가되어 코드가 받아져 있다.

n  여기서 npm install module_name –save    라고 쳐주면, package.json 파일에 dependencies 내용이 추가 된다. 즉 우리 프로젝트가 module_name 이라는 module xx 버전에 의존한다. 라고 표기 해준다.

dependencies
내용이 있으면, package.json 파일만 있으면 필요한 module 들을 쉽게 받아서 사용하게 될 수 있다.

결론 : module을 설치할 때,  --save 를 붙여주는 것이 좋다. (일시적으로 사용하는 , 소스코드에 추가할건 아니고, 잠시 코딩중에만 넣고자 하는 module이라면? –save 없이 install을 하고, 꼭 필요한 module이라면 –save를 붙여줘야 한다.

콜백 함수

l  다른 함수의 전달인자로 전달이 된다.

l  우리가 만든 함수이지만, 내가 직접 호출하는 함수가 아니다. 누군가에 의해 호출 당할 함수 이다.

l  Function b(v1,v2){}; A.sort(b) : 여기서 b가 콜백 함수 이다.

동기와 비동기 프로그래밍

l  NodeJS는 기본적으로 Event driven을 중심 개념으로 활용하기 때문에, 기본적으로 제공하는 함수들은 async로 한다. () filesystem.readFile() , readFileSync() 두 개가 존재하는데, 권장은 ReadFile() 사용이다. (Async)

l  Node js Single Thread로 동작한다. 그렇기 때문에 Async에 대해 정확한 이해가 필요하다.

Express

l   

 

 

 

설치

 

Node.js 설치 및 실행

l  Nodejs.org -> Download

Atom Editor 설치

l  https://atom.io/

l  소개 : https://opentutorials.org/module/1579

l  특징

n  무료

n  플러그인으로 기능을 쉽게 확장 할 수 있다.

n  HTMl , CSS, JavaScript와 같은 웹 기술로 화면을 구현하여 웹페이지를 편집하듯이 UI를 제어할 수 있는 특징이 있다.

NPM (Node Package Manager) : Node.js 설치하면 자동으로 설치된다.

l  http://www.Npmjs.com

Module 설치 , Proxy 설정 문제

l  npm ERR! network read ECONNRESET

n  npm config set proxy http://168.219.61.252:8080

n  npm config set https-proxy http://168.219.61.252:8080

n  npm config set strict-ssl false

 

 

 

 

 

React : 페이스북에서 개발한 유저인터페이스 라이브러리이다.

-       개발자로 하여금 재사용 가능한 UI 생성할 있게 해준다.

-       Virtual DOM이라는 개념을 사용하여 상태의 변화에 따라, 선택적으로 유저 인터페이스를 렌더링 한다. 최소한의 DOM 처리로 컴포넌트들을 업데이트 있게 해준다.

 

Sample Code

import React from 'react';

 

function HelloWorld () {

  return (

    <h1>Hello World!</h1>

  );

}

 

export default HelloWorld;

 

 

1.     Import React from ‘react’ (ES6 문법)

A.     코드는 실제로는 var React = require(‘react’) 같은 의미이다.(ES5 문법)

B.     이렇게 모듈을 require 하는 것은 Node.js 문법이다. (서버 개발 사용하는..) 클라이언트 쪽에선 보통 html 태그에서 script 통하여 파일들을 불러오지 require 쓰지 않고, 지원도 하지 않는다.
 
하지만, webpack 이라는 도구를 사용하여 마치 node.js 에서 require 하는 것과 같이 모듈을 불러올 있게 하는 것이다. Webpack 이렇게 import( or require) 모듈들을 불러와서 파일로 합쳐준다. (c에서 include 처럼.. )
 
작업을 번들링(Bundling) 이라고 한다.

2.     Return (<h1>HelloWorld</h1>)

A.     이런식으로 HTML 같은 코드가 ‘ , “ 없이 그냥 쓰이고 있는데, 코드는 JSX 코드이다.

B.     코드는 webpack 에서 번들링 과정을 거치면서 webpack에서 사용하는 babel-loader 통해 Javascript 변환된다. 아래는 JSX babel-loader 의해 변환된 JavaScript 모습

3.  return React.createElement(

4.      "h1",

5.      null,

6.      "Hello World!"

7.    );

C. export default HelloWorld : 다른 곳에서 import from ‘HelloWorld’ 같이 import 있도록 해준다.

 

Redux

 

Redux : Flux 개선형. Flux 같이 단방향 데이터 흐름을 보장하여 보다 예측하기 쉬운 구조를 개발자에게 제공한다. 점에서는 Flux 동일하다. Redux 개발한 개발자는 아래와 같이 Redux Flux 차이점을 서술 하고 있다.

(http://bestalign.github.io/2015/10/26/cartoon-intro-to-redux/)

Flux에서 몇몇 부분을 조금 바꾸면 나은 개발자 도구를 있으면서도 Flux 같은 예측 가능성을 가질 있다는 것을 발견했다.

 

 자세히 보면, 결국 예측 가능성을 가질 있는게 좋아진게 아니다. Flux 같은 수준의 예측 가능성이다. 같다고보면된다. 그러면 차이점은? 나은 개발자 도구. 개발하면서 개발을 편하게 해주는 여러 도구를 사용할 있기 때문에, 개발에 편하다는 것이다.

 결국 개발자 도구의 사용 편의성이 차이점이며, Redux 만든 사람은 아래의 가지를 핵심으로 얘기한다.

 

-      리로딩(hot reloading)

-       시간 여행 디버깅(time travel debugging)

 

앞으로 가지의 차이점을 중점적으로 Flux Redux 차이점을 살펴볼 예정이다.

 

Pure function : 전달인자로 전달받은 값을 덮어쓰지 않고, 단순 계산 등을 통한 값을 return 해준다

 Function squre(x){  return x * x; }

 

Impure function : 네트워크 혹은 데이터베이스를 사용하거나, 전달받은 값을 다른 값으로 덮어써서, 원본의 값을 변형 시키는 함수.

 Function squre(x)

{  updateXInDB(x); return x * x; }

 

 

Reducer : Pure function

 Store action 전달받은 다음, 받은 action root reducer에게 전달한다.

Root reducer 하위의 reducer들이 리턴한 clone state 객체를 combine 최종 app state 값을 store에게 return 한다. , 기존의 previous state 변경하지 않고, clone 새로운 new state 객체를 return 하므로, 우리는 reducer 순수함수. Pure function 이라고 말할 있다.

Smart Component , dumb component:

 Flux 에는 controller view view 있었다. Controller view view들을 가지고 있었으며,

Store 에서 State 바뀔 알려달라고 callback 등록하고 store 통신하는 친구가 바로 Controller view였다. 그래서 store state 바뀌었다고 알려오면, Controller view store로부터 새롭게 업데이트 state 받아와서는 자신에 포함되어 있는 일반 View들에게 prop 형태로 새로 받아온 state 전달하곤 했다.

 

Redux 에서는 Flux에서와 유사하지만, 이름이 다르다.

Smart Component, dumb component : 한국어로는 영민한 컴포넌트와 우직한 컴포넌트!

-       Component 서로 구분되기 위해, Cart Example에서는 Smart Component Container 라는 이름으로 사용하고, dumb component component 사용되었다.

-       .. Container라고 지은 이유는?.. 추측 해봤는데, 일반 view 유사한 Dumb component 들을 포함하고 있는. 가지고 있으면서 관리도 하기 때문에, Container라는 이름을 지은 것이 아닐까 생각해본다

(http://bestalign.github.io/2015/10/26/cartoon-intro-to-redux/)

 

l  Smart Component : Controller view 유사하다. 몇몇 규칙을 따른다.

n  액션 처리를 책임진다. Smart Component 내부적으로 아래 코드 처럼 dumb component 가지고 있는데, dumb component 에서 클릭이 되었을 , checkout 이라는 동작을 해야 한다면, 아래와 같이 smart component(container) dumb component에게, prop 형태로 checkout 함수를 콜백으로만 등록해준다.
 dumb component
입장에서는 어떤 액션을 보낼 필요가 있을 , smart component로부터 전달받은 함수를 단순히 호출만 해준다.

n  render() {

n      const { products, total } = this.props

n   

n      return (

n        <Cart

n          products={products}

n          total={total}

n          onCheckoutClicked={() => this.props.checkout()} />

n      )

n    }

n  Smart Component CSS style 가지고 있지 않다.

n  Smart Component 자신만의 DOM 거의 가지고 있지 않다. 대신 DOM 요소들을 관리하는 dumb component 관리한다.

l  우직한 컴포넌트는 액션에 직접 의존성을 가지지는 않는다. 이는 모든 액션을 props 통해서 넘겨받기 때문이다. 이말인 즉슨, 우직한 컴포넌트는 다른 로직을 가진 다른 애플리케이션에서 재사용될 있다는 뜻이다. 또한 어느정도 보기좋게 할만큼의 CSS style 포함하고 있다 (따로 style props 받아 기본 style 병합시켜서 style 바꿀수도 있다).

 

** Smart Component(Container) 코드를 살펴보면,

mapStateToProps 콜백함수를 정의하고 있고,

export default connect() 라는 코드를 있다. 이유는? 주석으로 적혀 있음.

 

//Smart Component 네트워크에 연결되면서, 새롭게 업데이트되어 전달될 state 가지고

//어떻게 자신이 가진 prop 업데이트 적용할지를 기술한 콜백 함수이다.

 

const mapStateToProps = (state) => {

  return {

    products: getCartProducts(state),

    total: getTotal(state)

  }

}

 

//Root Component 내에 있는 Provider 객체는 레이어 바인딩인데,

// 녀석이 기본적으로 Store State 업데이트가 되었음을 알려주면

// 컴퍼넌트들도 업데이트 있도록 네트워크를 생성한다.

 

//Smart Component Provider 생성해놓은 네트워크에 자신을 연결한다. Connect() 이용.

//이렇게 하면, 상태 업데이트를 받을 있게 된다.

 

export default connect(

  mapStateToProps,

  { checkout }

)(CartContainer)

 

:   레이어 바인딩이 생성해놓은 네트워크 (스토어의 state 업데이트를 받아 있도록.. 만든.. ) CartContainer 라는 이름의 Smart component 연결하라.!

 

 Store로부터 새로운 State 받아오면, Cart Container Prop 업데이트 건데, 업데이트를 위해 사용할 콜백함수는 ‘mapStateToProps’ 이다.!

 

 라고 네트워크에 연결하면서 설정해놓는다.

 

 이제 레이어 바인딩 Provider 객체 덕분에 Store state 변경 내용을 Smart Component 전달받게 되고, smart component 자신의 prop 업데이트 , 자신이 소유하고 있는 dumb component 들에게 prop 이어서 전달하므로, 각각의 view. dumb component 들은 새롭게 전달받은 prop 데이터를 이용해서 view 업데이트 한다.

 

 

 

 

View Layer Binding ( 레이어 바인딩)

 Store View 연결하기 위해서는 Redux 도움이 필요하다.

  둘을 함께 묶어줄 무언가가 필요한데, 이걸 해결해주는 것이 바로 레이어 바인딩이다.

 React 사용한다면 react-redux 그것이다.

 (Redux shopping cart 예제에서는 react-redux 로부터 얻은 Provider 라는 객체를 사용한다.)

 

레이어 바인딩은 모든 컴퍼넌트를 스토어에 연결하는 역할을 한다.

<Provider store={store}>

    <App />

  </Provider>

 

Provider 레이어 바인딩이며, store 아래 <App /> 객체를 연결해준다.

다른 말로, App 내부적으로 Container(smart component) , dumb component . View 들을 모두 들고 있는 객체이므로, Store 모든 컴퍼넌트들을 연결하는 역할을 한다고 있다.

 

레이어 바인딩은 개의 컨셉을 가지고 있다: (Provier 객체가 설명에 등장한다.!!)

1.       공급 컴포넌트(provider component): 컴포넌트 트리를 감싸는 컴포넌트이다connect() 이용해 루트 컴포넌트 밑의 컴포넌트들이 스토어에 연결되기 쉽게 만들어준다.

2.       connect(): react-redux 제공하는 함수이다. 컴포넌트가 애플리케이션 상태 업데이트를 받고 싶으면 connect() 이용해서 컴포넌트를 감싸주면 된다. 그러면 connect() 셀렉터(select) 이용해서 필요한 모든 연결을 만들어준다.

3.       셀렉터(selector): 직접 만들어야 하는 함수이다. 애플리케이션 상태 안의 어느 부분이 컴포넌트에 props로써 필요한 것인지 지정한다.

 

 

Root component:

 루트 컴포넌트는 모든 팀이 일을 하도록 하는 임무를 가진다. 스토어를 생성하고 무슨 리듀서를 사용할지 알려주며 레이어 바인딩과 뷰를 불러온다.

하지만 루트 컴포넌트는 애플리케이션을 초기화한 뒤로는 거의 하는 일이 없다. 화면을 갱신도 더는 신경 쓰지 않는다. 화면 갱신은 레이어 바인딩의 도움으로 아래의 컴포넌트들이 맡아서 처리한다.

 정리 : 루트 컴포넌트는 공급 컴포넌트로 서브 컴포넌트를 감싸고 스토어와 공급 컴포넌트 사이를 연결한다. 

render(

  <Provider store={store}>

    <App />

  </Provider>,

  document.getElementById('root')

)

 

 우리의 shopping cart example 에서는 index.js 파일이 root component 이다.

 

내용까지를 가지고, 아래의 내용을 읽어보면 것이다. 아래의 내용은 모두 http://bestalign.github.io/2015/10/26/cartoon-intro-to-redux/ 사이트 내용을 그대로 복사 것이며, 내용을 이해하고 보거나, 아래 내용을 읽고 내용을 읽으면 이해하는데 도움이 것이다.

 

어떻게 함께 동작하는가

애플리케이션을 생성하기 위해 어떻게 캐릭터들이 함께 일하는지 살펴보자.

준비(the setup)

준비 단계에서는 애플리케이션의 여러 부분이 모두 함께 연결된다.

1.     스토어를 준비한다. 루트 컴포넌트는 createStore() 이용해서 스토어를 생성하고 무슨 리듀서를 사용할지 알려준다. 루트 컴포넌트는 이미 필요한 모든 리듀서를 가지고 있다combineReducers() 이용해서 다수의 리듀서를 하나로 묶는다.  

2.     스토어와 컴포넌트 사이의 커뮤니케이션을 준비한다. 루트 컴포넌트는 공급 컴포넌트로 서브 컴포넌트를 감싸고 스토어와 공급 컴포넌트 사이를 연결한다

공급 컴포넌트는 기본적으로 컴포넌트를 업데이트하기 위한 네트워크를 생성한다. 영민한 컴포넌트는 connect() 네트워크에 연결한다. 이렇게 상태 업데이트를 받을 있게 만든다.  

3.     액션 콜백(action callback) 준비한다. 우직한 컴포넌트가 액션과 쉽게 일할 있게 하기 위해 영민한 컴포넌트는 bindActionCreators() 액션 콜백을 준비한다. 이렇게 간단히 콜백을 우직한 컴포넌트에 넘겨줄 있다. 액션은 포맷이 바뀐 자동적으로 보내진다.  

데이터 흐름(the data flow)

이제 사용자가 애플리케이션을 사용할 준비가 되었다. 액션을 하나 보내고 데이터 흐름을 확인해보자.

1.     뷰가 액션을 요청한다. 액션 생성자가 포맷을 변환한 돌려준다.

  

2.     bindActionCreators() 준비과정에서 사용되었으면 자동으로 액션이 보내진다. 그게 아니라면 뷰가 직접 액션을 보낸다.

  

3.     스토어가 액션을 받는다. 현재 애플리케이션 상태 트리와 액션을 루트 리듀서에게 보낸다.

  

4.     루트 리듀서는 상태 트리를 조각으로 나눈 알맞은 서브 리듀서로 상태 조각들을 넘겨준다.

  

5.     서브 리듀서는 받은 상태 조각을 복사한 , 복사본을 변경한다. 루트 리듀서에게 변경된 복사본을 돌려준다.

  

6.     모든 서브 리듀서가 변경 상태 조각들을 돌려주면, 루트 리듀서는 상태 조각들을 한데 모아 상태 트리로 만든 스토어로 돌려준다. 스토어는 새로운 상태 트리를 옛날 상태 트리와 바꾼다.

  

7.     스토어는 레이어 바인딩에게 애플리케이션 상태가 변경되었다는 것을 알린다.

  

8.     레이어 바인딩은 스토어에게 새로운 상태를 보내달라고 요청한다.

  

9.     레이어 바인딩은 뷰에게 화면을 업데이트하도록 요청한다.

  

 

 

블로그 이미지

kuku_dass

,