이 글은 자바스크립트 개론(Eloquent JavaScript 번역서)를 읽고 개인적으로 정리한 내용입니다.

4. 오류 처리

코드 상의 오류

  • 자바스크립트에서는 함수에 인자를 더 적게 전달하더라도 에러가 나지 않고 undefined이 전달될 뿐이다. 그리고 undefined이 담긴 매개변수로 수식 연산을 한다고 해도 오류가 발생되지 않고 결과는 NaN이 된다. 
  • 반면 오류를 인식하는 경우도 있다. 예로 undefined에서 프로퍼티를 읽으려 하거나 존재하지 않는 함수를 호출할 때 자바스크립트는 오류임을 알린다.

외부 입력에 의한 오류

  • 보통 사용자의 입력에 의해 일어나며 유효성 검증을 통해 무엇이 잘못되었는지 알려야 한다.

값을 반환하여 오류처리

  • 예로 정상적으로 동작할 때 문자열을 반환하는 함수는 잘못된 인자가 전달될 경우에 undefined을 반환하도록 할 수 있다.
  • 하지만 이 방법은 모든 타입의 값을 반환할 수 있는 함수라면, 어떤 값이 반환됐을 때 오류가 발생한 것인지 구별하기 어렵다.
  • 또 함수를 호출하는 모든 곳에서 오류 검사 코드를 작성해야 하는 단점도 있다.

예외 처리

  • 예외를 발생시키게(던지게) 되면 현재 실행 흐름에서 벗어나는 것과 동시에 함수가 호출되면서 쌓여있던 호출 스택을 풀어버린다.
  • try 문은 예외가 발생했을 때 호출 스택이 모두 풀어지지 않게 오류가 발생하는 지점을 감싼다.
  • catch문은 try에서 발생한 예외를 잡아서 처리한다.
  • finally문은 예외 발생여부와 관계없이 실행되어야 하는 코드를 작성할 수 있다.
function sum(a, b) {
    if(typeof a != 'number' || typeof b != 'number') {
        throw '숫자를 전달하세요.';
    } else {
        return a + b;
    }
}

function callSum(a, b) {
    return sum(a, b);
}

try {
    callSum(1, '1');
} catch (error) {
    console.log('에러 발생', error);
} finally {
    console.log('언제나 실행');
}

오류 객체

  • new 키워드와 Error 생성자를 이용하여 자바스크립트의 기본 에러 메시지와 비슷한 메시지를 출력할 수 있다.
try {
    throw new Error('custom error object');
} catch (e) {
    console.log(e.message); // 'custom error object'
}


이 글은 자바스크립트 개론(Eloquent JavaScript 번역서)를 읽고 개인적으로 정리한 내용입니다.

3. 객체와 배열

1. 프로퍼티

  • 자바스크립트의 어떤 값은 그 값과 관련된 다른 값을 갖기도 하는데, 이것을 프로퍼티라고 한다.
  • 예로 문자열 값에는 length라는 문자열의 크기를 나타내는 프로퍼티가 있다.

2. 객체

  • 문자열과 같은 값은 프로퍼티를 추가하거나 수정하는 것에는 제한적이다. 예로 문자열의 크기를 나타내는 length 프로퍼티는 변경할 수 없다.
  • 객체는 자유롭게 프로퍼티를 추가, 수정, 제거를 할 수 있다.
  • 객체의 프로퍼티에 접근하는 방법은 두 가지가 있다.
var object = { name: 'gyun' };
object['name'] // 'gyun'
object.name // 'gyun'
  • 대괄호를 이용한 방법은 대괄호 안에 표현식을 넣어 접근할 수도 있다.
var s = 'name';
object[s] // 'gyun'

3. 객체 비교

  • 숫자, 불린, 문자열 값은 같은 타입의 값을 비교하면 정확한 결과를 얻을 수 있다.
120 == 120 // true
true == true // true
'str' == 'str' // true
  • 두 객체에 == 연산자를 적용하면 생각했던 것과 다른 결과가 나올 수 있다.
var object1 = {name: 'gyun'};
var object2 = object1;
object1 == object2 // true

var object3 = {name: 'gyun'};
object1 == object3 // false
  • var object2 = object1에서는 {name: 'gyun'} 객체가 담기는 것이 아니라 object1의 주소값이 object2에 담긴다. object1object2의 주소값이 같기 때문에 object1 == object2true를 반환한다.
  • object3object1와 프로퍼티, 프로퍼티 값이 같지만 새로운 객체의 주소값이 담긴다. 따라서 object1 == object3false를 반환한다.

4. 배열

  • 객체에 저장된 모든 프로퍼티를 처음부터 끝까지 접근하기 좋게 만든 것이 배열이다.
  • 자바스크립트의 배열은 객체의 일종이다.

5. 메소드

  • 함수를 담고 있는 프로퍼티를 메소드라고 한다.

6. 날짜

  • 객체를 생성하는 방법에는 중괄호({ })말고도 new키워드와 생성자 함수를 이용한 방법도 있다.
  • Date는 자바스크립트에서 날짜 데이터를 다루기 위해 사용되는 생성자 함수다.
new Date() // 현재 시간의 날짜 객체 생성
new Date(2017, 11, 25) // 2017년 11월 25일의 날짜 객체 생성
  • Date 생성자 함수에는 년, 월, 일, 시, 분, 초, 밀리초 순서로 인자를 넘길 수 있다.
  • 날짜 객체는 <, >, <=, >=로 정확하게 날짜의 늦고 빠름을 정확하게 비교할 수 있지만, ==은 객체처럼 비교하기 때문에 정확한 결과를 얻을 수 없다.
  • 날짜가 같은지 비교할 때는 1970년 1월 1일부터 경과된 밀리초를 정수로 반환하는 getTime 메서드를 사용하면 된다.
var d1 = new Date(2017, 11, 25);
var d2 = new Date(2017, 11, 25);

d1 == d2 // false
d1.getTime() == d2.getTime() // true

7. 인자

  • 함수가 호출될 때마다 arguments라는 특별한 변수가 함수가 실행되는 환경에 담긴다.
  • arguments는 함수로 전달되는 모든 인자를 프로퍼티 0부터 순서대로 저장하는 것, 인자의 개수가 length 프로퍼티에 담기는 것을 봤을 때 배열이라고 생각할 수도 있다.
  • 하지만 arguments는 배열이 아니라 객체이고, 배열처럼 push, forEach와 같은 메서드를 사용할 수 없다.


이 글은 자바스크립트 개론(Eloquent JavaScript 번역서)을 읽고 개인적으로 정리한 내용입니다.


2. 함수

1. 선언

  • 프로그램이 실행되기 전에 함수 선언은 미리 저장된다.
  • 프로그램이 실행될 때 함수 선언문이 나오지 않았더라도 먼저 호출할 수 있으며, 먼저 선언된 함수가 나중에 선언된 함수를 호출하는 것도 가능하다.

2. 유효범위(Scope)

  • 함수의 인자(parameter)와 함수 안에서 var 키워드로 선언된 변수는 그 함수 안에서만 유효하다. 함수 실행이 완료되면 인자, 변수는 사라진다.
  • 함수가 중첩된 경우 내부 함수안에서 선언된 변수 이름과 외부 함수에서 선언된 변수 이름이 같아도, 내부 함수의 변수를 변경하여도 외부 함수의 변수에 영향을 미치지 않는다.
  • 함수 안에서 같은 이름의 변수를 선언하지 않았으면, 외부 함수나 전역 변수에 접근하는 것이 가능하다.
  • 함수의 스코프는 프로그램 소스코드 상에서의 위치에 따라 결정되며, 이것을 어휘적 스코프(lexical scope)라고 한다.

호출 스택

  • 새로운 함수가 호출되고 그 함수의 실행이 종료되면 원래 프로그램이 실행되던 지점으로 돌아갈 필요가 있다. 기존의 실행지점에 대한 정보를 문맥(Context)이라고 하며 이것은 스택(Stack)에 쌓인다.

값으로서의 함수

  • 자바스크립트에서는 함수도 ‘값’에 속해서 함수 이름을 변수처럼 쓰거나 함수를 다른 함수에 전달하거나 함수를 반환하는 함수를 작성할 수도 있다.

클로저

  • 함수가 함수를 반환하고, 반환되는 함수에서 함수가 속한 함수의 지역변수를 반환할 때 외부 함수의 실행이 종료되서 호출스택에서 사라져도 지역변수는 유지된다.
  • 이 현상을 닫힘(closure)라고 하며, 남아있는 변수에 접근하는 함수를 클로저(closure)라고 한다.

인자

  • 선언된 매개변수(parameter)보다 더 적은 인자(argument)를 넘겨주면 남는 매개변수(parameter)는 undefined가 된다.
  • 선언된 매개변수(parameter)보다 더 많은 인자(argument)를 넘겨주면 남는 인자(argument)는 무시된다.

함수 작성 원칙

  • 함수가 발명된 이유는 코드의 재사용이다.
  • 함수를 사용하지 않고, 동일한 로직이 반복된다면 코드가 길어져서 읽기가 힘들어지고, 코드를 수정할 때 일일이 다 찾아야 하기 때문에 유지보수가 어려워진다.
  • 함수는 되도록 한 개의 기능만 수행해야 여러 상황에서 사용되기 좋다.
  • 확실하게 필요하다고 판단하지 않은 기발한 기능은 되도록 함수에 추가하지 않는 것이 좋다.

순수 함수

  • 순수 함수란 동일한 인자를 전달하면 항상 같은 값을 반환하는 함수를 말한다.
  • 순수 함수는 함수 호출만으로 해당 함수가 의도대로 동작하는지 확인할 수 있기 떄문에 테스트하기 좋다.
  • 순수하지 않은 함수는 온갖 외부 요인에 따라 함수의 결과값이 바뀌기 때문에, 해당 함수만을 호출하여 의도대로 동작하는지 확인하기 힘들다.
  • 순수 함수는 순수하지 않은 함수의 기능을 대체하기 힘들기 때문에 순수하지 않은 함수를 작성하는 것을 꺼려하지 말되, 되도록 순수한 함수를 작성하도록 하자.

재귀(Recursive)

  • 재귀란 함수 자기 자신을 호출하는 것을 말한다.
  • 재귀를 통해 반복문을 대체할 수 있지만 재귀는 반복문보다 10배정도 느리며, 재귀 호출을 하는 횟수가 많아지면 호출 스택이 넘치는 현상이 발생할 수 있다.


이 글은 자바스크립트 개론을 읽고 개인적으로 정리한 내용입니다.


자바스크립트 기초: 값, 변수, 제어 흐름

값의 타입 6가지

  • 숫자, 불리언, 문자열, 객체, 함수, undefined

숫자

  • 자바스크립트는 숫자를 내부적으로 64비트 부동소수점 값으로 표현한다. 다른 언어처럼 정수를 위한 특별한 자료형이 없다. 즉, 정수(Integer)든 소수(Decimal)든 간에 부동 소수점 방식으로 표현되는 것이다.

  • 정수(Integer)도 부동 소수점으로 표현된다는 것은 수의 정확도가 제한됨을 의미한다. 단, 52개의 비트로 표현될 수 있는 정수는 항상 정확하게 계산된다.

  • 하지만 소수(Decimal)의 경우에는 항상 값이 근사치임을 알고 있어야 한다.

문자열

  • 문자열 안에서 이스케이프 기호 \를 사용하면, \ 다음에 오는 문자는 특별한 의미가 있음을 알릴 수 있다. 주로 따옴표' 혹은 쌍따옴표("), 줄바꿈, 탭을 문자열 안에서 표현할 때 사용된다.

  • 문자열을 대상으로 비교 연산자(>, <, >=, <=, ==)를 사용하면 유니코드를 기준으로 비교 대상의 문자열의 왼쪽 문자부터 비교한다.

표현식(expression)과 문장(statement)

  • 값을 생성하는 코드조각을 표현식(expression)이라 한다.
  • 화면에 문자가 출력되거나, 변수에 저장된 값을 변경하는 것처럼 프로그램 상에서 부수효과(side effect)를 일으키는 코드 조각을 문(statement)이라 한다.

환경(environment)

  • 특정 시점에 존재하는 변수의 값, 변수의 모음이 있는 공간을 환경이라 한다.
  • 프로그램이 구동되면 개발자가 선언한 변수 외에도 브라우저의 표준 변수가 환경에 담긴다.
  • 환경은 다른 페이지로 이동할때까지 유지된다.

함수

  • 함수는 표준 환경에서 제공되는 값이다.
  • 함수는 코드 조각을 담을 수 있으며, 함수값(함수명)은 그런 코드 조각을 가르킨다.
  • 함수에 담긴 코드를 실행하는 것은 호출(invoke)하거나 적용(apply)한다고 한다.
  • 함수를 호출할 때는 값을 넘겨줄 수 있다.
    호출하는 쪽에서 넘기는 값들은 인자(argument)라고 한다.
    선언하는 쪽에서 받는 값들은 매개변수(parameter)라고 한다.

반복문

  • 반복문의 종류에는 while, for, do while이 있다.
  • while, for는 조건이 만족되지 않으면 반복문의 내용이 한 번도 실행되지 않는다.
  • do while은 조건이 만족되든 안되든 반복문의 내용을 무조건 1번은 실행한다.

undefined

  • 값이 할당되지 않는 변수는 undefined라는 특별한 값을 얻는다.
  • 코드 상에서 값을 반환하지 않는 함수도 undefined반환한다.
  • 비슷한 값으로 null이 있는데, null은 값이 정의되었지만 아직 값이 없음을 알린다.
  • 참고로 undefined == null은 true를 반환한다.

형변환

  • 자바스크립트에서 연산자의 피연산자는 자동 형변환이 일어날 수 있다. 두 값중 다른 값의 타입으로 변환이 된다.
  • 조건문, 반복문의 조건식의 값이 불리언이 아니면 자동으로 불리언으로 형변환된다.
  • 문자열을 대상으로 ‘+’가 아닌 산술 연산자가 사용되면 문자열은 숫자로 형변환된다. 이 때, 숫자가 될 수 없는 문자열을 NaN이 된다. 문자열이 숫자가 될 수 없는지 판단하기 위해 NaN와 직접 비교하는 것은 정확하지 않다. 변수에 NaN가 있는지 판단할 때는 isNaN 함수를 사용하자.

&& 와 ||

  • AND(&&)와 OR(||) 연산자는 불리언 값을 반환하지 않고, 정확히 말하면 피연산자를 반환한다.
  • OR(||)은 왼쪽의 값을 보고, 그 값이 불리언으로 변환됐을 때 false이면 오른쪽의 값을 살핀다. 만약 왼쪽의 값이 true이면 오른쪽 값을 살피지 않고 왼쪽의 값을 반환한다. 
  • AND(&&)는 왼쪽의 값을 보고, 그 값이 불리언으로 변환했을 때 true이면 오른쪽의 값을 살핀다. 만약 왼쪽의 값이 false이면 오른쪽 값을 살피지 않고 false를 반환한다

기타

  • 문자열을 정수로 변환할 때는 Number 객체를 사용한다. parseInt는 문자열을 해석(parse)할 때 사용한다.
  • 여기서 말하는 window.isNaN은 부정확하다. 대신에 NaN를 비교할 때는 ES6에서 포함된 Number.isNaN를 사용하면 된다.


 프론트 개발을 하면서 웹 상에 돌아다니는 코드를 보면 배열의 마지막에 쉼표를 찍는 경우를 종종 봤었는데 왜 이걸 쓰는지 이유는 모르고 그냥 넘어갔던 것 같다. 의외로 큰 뜻이 있으면 어떡하나 싶어서 찾아봤는데, 그다지 중요한 문제는 아닌 것 같다... 이 글은 MDN에 있는 Trailing Commas를 번역한 자료이다.


Trailing commas

Trailing comma(후행 쉼표)는 새로운 배열 요소, 파라메터, 프로퍼티를 자바스크립트 코드에 추가할 때 유용하다. 만약 새 프로퍼티를 더하고 싶고, 마지막 줄에 쉼표가 있다면 이전의 마지막 줄을 수정하지 않고 새로운 라인을 간단하게 추가할 수 있다. 이것은 버전관리 도구에서의 비교를 명확하게 하고, 코드 편집을 덜 귀찮게 할 것이다.

자바스크립트는 태어날 때부터 리터럴의 후행 쉼표를 허용해줬다. 그리고 ECMAScript 5에서는 객체 리터럴, ECMAScript 2017에서는 함수 파라메터에 후행 쉼표를 추가하는 것이 허용됐다.

그러나 JSON은 후행 쉼표를 허용하지 않는다.



배열, 객체 리터럴의 후행 쉼표

배열

자바스크립트는 배열의 후행 쉼표를 무시한다.

var arr = [
    1,
    2,
    3,
];

arr; // [1, 2, 3]
arr.length; // 3

만약 후행 쉼표가 한 개이상 사용되면 배열에 구멍이 생긴다. 구멍이 있는 배열은 희소(sparse) 하다고 불린다. 조밀한(dense) 배열은 구멍이 없다. Array.prototype.forEach( ) 혹은 Array.prototype.map( )으로 배열을 순회할 때는 배열의 구멍을 건너뛴다.

객체

ECMAScript 5부터는 객체 리터럴에도 후행 쉼표가 유효하다.

var object = {
    foo: "bar",
    baz: "qwerty",
    age: 42,
}; 


함수의 후행 쉼표

ECMAScript 2017은 함수 파라메터 목록에 후행 쉼표를 허용한다.

파라메터 정의

다음의 함수 정의 쌍들은 유효하며 서로 같은 의미다. 후행 쉼표는 함수 정의의 length 프로퍼티 혹은 arguments 객체에 영향을 주지 않는다.

function f(p) {}
function f(p, ) {}

(p) => {};
(p, ) => {};

후행 쉼표는 클래스나 객체의 메서드 정의에도 동작한다.

class C {
    one(a,) {},
    two(a, b,) {},
}

var obj = {
    one(a,) {},
    two(a, b,) {},
};

함수 호출

다음의 함수 호출 쌍들은 유효하며 서로 같은 의미다.

f(p);
f(p, );

Math.max(10, 20);
Math.max(10, 20,);

유효하지 않은 후행 쉼표

함수 파라메터 정의나 함수 호출이 쉼표만을 포함한다면 SyntaxError가 발생한다. 그리고 나머지 매개변수(rest parameter)를 사용할 때 마지막 쉼표는 허용되지 않는다.

function f(,) {} // SyntaxError: missing formal parameter
(,) => {};       // SyntaxError: expected expression, got ','
f(,)             // SyntaxError: expected expression, got ','

function f(...p,) {} // SyntaxError: parameter after rest parameter
(...p,) => {}        // SyntaxError: expected closing parenthesis, got ','


비구조화(destructuring)에서 후행 쉼표

비구조화 할당을 사용할 때는 후행 쉼표가 왼편에서만 허용된다.

// 후행 쉼표로 배열 비구조화
[a, b,] = [1, 2];

// 후행 쉼표로 객체 비구조화
var o = {
    p: 42,
    q: true,
};

var {p, q,} = o;

나머지 요소(rest element)를 사용할 때는 SyntaxError가 발생한다.

var [a, ...b,] = [1, 2, 3];
// SyntaxError: rest element may not have a trailing comma


JSON에서 후행 쉼표

객체의 후행 쉼표는 ECMAScript 5에서 소개됐다. JSON은 ES5 이전의 자바스크립트 문법을 기반으로 하기 때문에, JSON에서는 후행 쉼표가 허용되지 않는다.

두 라인은 모두 SyntaxError가 발생한다.

JSON.parse('[1, 2, 3, 4, ]');
JSON.parse('{"foo" : 1, }');
// SyntaxError JSON.parse: unexpected character
// at line 1 column 14 of the JSON data

JSON을 정확하게 파싱하기 위해 후행 쉼표를 생략해라.

JSON.parse('[1, 2, 3, 4 ]');
JSON.parse('{"foo" : 1 }');


'JavaScript' 카테고리의 다른 글

[JavaScript] 변수 스코프  (0) 2017.04.13
[JavaScript] 생성자 함수  (0) 2017.02.27
[JavaScript] 배열 연결  (0) 2017.01.26
[JavaScript] 동등, 일치 연산자  (0) 2016.10.04

이 글은 EcmaScript5를 기준으로 작성되었습니다.

JavaScript 스코프

변수 스코프?

변수 스코프란, 변수가 선언된 지점을 기준으로 변수에 접근할 수 있는 범위를 말하며, 다른 말로 유효범위라고도 한다. 다른 언어에서는 블록({})을 기준으로 변수 스코프가 형성되는 경우가 있지만, 자바스크립트는 아니다.

if(true) {
    var aa = 1;
}

console.log(aa);

실행결과

> 1

블록 안에서 선언된 변수 aa를 블록 밖에서도 접근이 가능하다.

스코프의 종류

자바스크립트에서 형성될 수 있는 스코프는 두 가지이다.

1. 함수 스코프

말 그대로 함수를 기준으로 스코프가 형성되는 것을 말한다. 함수 내부에서 선언된 변수는 외부에서 접근할 수 없다.

function myFunc() {
    var aa = 1;
}

console.log(aa); // Error!!

반대로 외부의 변수는 내부 함수에서 접근할 수 있다.

var aa = 1;

function myFunc() {
    console.log(aa);
}

myFunc(); // 1

2. 전역 스코프

전역 스코프(global scope)는 함수 밖의 영역을 말한다. 전역 스코프의 변수는 모든 함수 스코프에서 참조할 수 있다.

/* global scope */
var aa = 1;

function myFunc() {
    /* function scope */
    console.log(aa);
}

function anotherFunc() {
    /* function scope */
    console.log(aa);
}

myFunc();
anotherFunc();

실행결과

> 1
> 1

전역 스코프에 선언된 변수는 다른 자바스크립트 파일에서도 접근할 수 있다.

/* index.html */
/*
    <html>
    <head>
        <script src='myModule.js'></script>
        <script src='script.js'></script>
    </head>
    </html>
*/

/* myModule.js */
var a = 1;
console.log(b);

/* script.js */
var b = 2;
console.log(a);

실행결과

> Error! b is not defined
> 1

<script> 태그로 로딩되는 순서에 따라서 파일 사이에 전역 스코프의 접근이 가능해진다. 전역 스코프에 변수를 선언하게 되면 자바스크립트 파일간 영향을 미칠 수 있기 때문에(특히 써드파티 모듈을 사용하는 경우), 신중하게 다뤄야 한다.

호이스팅(Hoisting)

호이스팅(Hoisting, 끌어올림)이란 변수 선언문이 끌어올려지는 현상을 말한다.

/* 호이스팅이 일어나기 전 */
console.log(aa);
if(true) {
    var aa = 1;
}
console.log(aa);

function myFunc() {
    console.log(bb);
    var bb = 2;
    console.log(bb);
}

myFunc();

위의 코드는 변수 호이스팅에 의해 자바스크립트 엔진에서 이렇게 해석된다.

/* 호이스팅이 일어난 후 */
var aa;
console.log(aa);
if(true) {
    aa = 1;
}
console.log(aa);

function myFunc() {
    var bb;
    console.log(bb);
    bb = 2;
    console.log(bb);
}

myFunc();

실행 결과

> undefined
> 1
> undefined
> 2

전역 스코프에 있는 변수의 선언문은 자신이 속한 자바스크립트 파일의 최상단으로, 함수 스코프에 있는 변수의 선언문은 함수 스코프의 최상단으로 끌어올려진다.

주의해야 할 점은 선언문만 끌어올려지고, 할당문은 그대로 있는 것이다. 때문에 첫 번째 aa에 접근할 때는 aa가 아직 초기화되지 않았으므로 undefined이 출력된다.

참고자료


'JavaScript' 카테고리의 다른 글

[JavaScript] Trailing Comma 번역자료  (0) 2017.10.02
[JavaScript] 생성자 함수  (0) 2017.02.27
[JavaScript] 배열 연결  (0) 2017.01.26
[JavaScript] 동등, 일치 연산자  (0) 2016.10.04

동전 문제

https://www.acmicpc.net/problem/9084

첫 번째 시도

동적 프로그래밍으로 풀고 싶었다.
동전의 개수 N = 2, 동전의 종류 C = [1,2], 만들어야 할 금액 M = 1000가 입력된다고 하고.

D[1]은 가능한 모든 동전을 사용해 1원을 만들 수 있는 경우의 수
D[2]은 가능한 모든 동전을 사용해 2원을 만들 수 있는 경우의 수
….
D[M]은 가능한 모든 동전을 사용해 M원을 만들 수 있는 경우의 수

여기서 D[M]을 구하는 일반화된 식을 만들어보려고 M=1인 경우부터 쭉 나열했다.


이런식으로 표를 작성했는데, 경우의 수가 중복해서 계산되는 문제가 있었다.

두 번째 시도

이상한 표만 계속 그리다가 결국 구글링..

http://blog.naver.com/occidere/220793012348 이 글이 가장 도움이 됐다. 코드는 안보고(!) 일반화하는 방법을 참고했다.


동전의 종류가 오름차순으로 주어지는 것에 가장 큰 힌트가 있었다.
동전 1, 2가 있다면

  1. 동전 1원으로, 1원부터 M원까지 만드는 경우의 수를 구하고, 결과를 D[M]에 저장.


  2. 동전 1원과 2원으로, 1원부터 M원을 만드는 경우의 수를 구한다. 앞 단계에서 구했던 D[M]을 재사용한다 !!


 한 마디로 앞 단계에서 사용 가능한 동전(1원)으로 M원을 만드는 경우의 수에, 현재 단계에서 새로 추가된 동전(2원)으로 M원을 만드는 경우의 수를 더한다.


최종 코드

T = int(input())

for _ in range(T):
    N = int(input())
    C = list(map(int, input().split()))
    M = int(input())

    d = [0 for _ in range(M + 1)]

    d[0] = 1

    for coin in C:
        for m in range(coin, M + 1):
            d[m] = d[m] + d[m - coin]

    print(d.pop())


'ETC' 카테고리의 다른 글

Authentication VS Authorization  (0) 2015.03.26

자바스크립트 생성자 함수

자바스크립트는 ‘생성자 함수’에 인스턴스에 만들어질 속성과 메서드를 정의하고, ‘생성자 함수’를 이용해서 인스턴스를 생성할 수 있다.

function Person(name) [
    this.name = name;
}

Person은 생성자 함수이다. 자바스크립트의 함수는 반복되는 로직을 모아놓은 함수가 될 수도, 인스턴스의 모양(속성이나 메서드)이 정의된 생성자가 될 수도 있다. 다른 언어에서는 클래스와 생성자가 구분되지만, 자바스크립트에서는 생성자 함수가 클래스의 역할을 한다. 그리고 생성자 함수의 이름은 Person처럼 첫 글자를 대문자로 쓰는 것이 관례이다.

인스턴스를 만들 때는 new 연산자와 함께 생성자를 호출한다.

var tarzan = new Person('Tarzan');
tarzan.name; // 'Tarzan'

주의할 것은 new 연산자가 빠지면 안되는 점이다. 그냥 Person() 으로 호출하면 Person은 일반 함수로서 동작한다. 일반 함수의 this는 기본적으로 글로벌 객체를 참조하며 strict-mode에서는 undefined이 된다.

이어서 메서드의 정의는 생성자 안에서 할 수도 있다. 하지만 비효율적이다.

function Person(name) {
    this.name = name;
    this.describe = function() {
        return 'my name is ' + this.name;
    }
}

new 연산자는 생성자의 this가 새로운 인스턴스를 가르키게 한다. 그리고 this로 참조하는 인스턴스에 속성과 메서드를 추가할 것이다. 위의 예제에서도 Person으로 생성하는 모든 인스턴스에서 name 속성과 describe 메서드를 추가한다. 그런데 name 속성은 인스턴스마다 모두 달라서 인스턴스에 추가되는 것이 맞지만, describe 메서드는 모든 인스턴스에 추가될 필요가 없다. 한 공간에 메서드를 한번만 정의하고, 모든 인스턴스에서 그 공간을 참조할 수 있게 하는 방법이 더 효율적이다.

function Person(name) {
    this.name = name;
}

Person.prototype.describe = function() {
     return 'my name is ' + this.name;
}

var tarzan = new Person('tarzan');
var jane = new Person('jane');

tarzan.describe(); // 'my name is tarzan'
jane.describeS(); // 'my name is jane' 

Person.prototype  객체에 메서드를 추가하면 모든 인스턴스에서 메서드를 공유할 수 있다. 

프로토타입

자바스크립트의 원시 값을 제외한 모든 객체는 코드 상에서 접근할 수 없는 내부 속성인 [[Prototype]] 속성을 가진다. [[Prototype]] 속성은 자신의 프로토타입 객체를 가르킨다. 프로토타입 객체란 자신의 부모 객체를 말한다. 그리고 [[Prototype]] 속성으로 연결된 두 객체의 관계를 프로토타입 관계(혹은 상속 관계)라고 부르며, 하나 이상의 프로토타입 관계는 프로토타입 체인을 형성한다. 그리고 자식 객체에서 동일한 프로토타입 체인에 있는 부모 객체의 속성이나 메서드에 접근할 수 있다.

Person의 인스턴스인 tarzan과 jane에도 역시 [[Prototype]]속성이 있다. 그리고 생성자가 new 연산자와 함께 실행될 때, 새로운 인스턴스인 tarzan과 jane의 [[Prototype]]속성은  Person.prototype 객체를 참조하게 된다. 즉, 인스턴스와 Person.prototype 객체는 프로토타입 관계에 있다. 인스턴스에는 describe 메서드가 직접 선언되어 있지 않지만, 프로토타입 체인을 통해 Person.prototype의 describe 메서드에 접근할 수 있다.

Person.prototype은 이름만 봤을 때 Person의 프로토타입 객체로 착각하기 쉽다. Person의 프로토타입 객체는 생성자 함수이기 이전에 일반 함수이기 때문에 자바스크립트 내장 객체인 Function이며, Person.prototype은 Person 인스턴스들의 프로토타입 객체(인스턴스 프로토타입)이다.

참고

  • 악셀 라우슈마이어의 자바스크립트를 말하다
  • MDN - 객체 지향 자바스크립트 소개
  • 생활코딩 - 생성자와 new


'JavaScript' 카테고리의 다른 글

[JavaScript] Trailing Comma 번역자료  (0) 2017.10.02
[JavaScript] 변수 스코프  (0) 2017.04.13
[JavaScript] 배열 연결  (0) 2017.01.26
[JavaScript] 동등, 일치 연산자  (0) 2016.10.04

자바스크립트 배열 연결하기.

 JavaScript의 Array 객체는 두 개의 배열을 연결할 수 있는 concat 메서드를 제공한다.

var arr1 = [1, 2, 3];
var arr2 = ['a', 'b', 'c'];

var new_arr = arr.concat(arr2);
// arr1 뒤에 arr2를 이은 새로운 배열을 반환한다.

// arr1 : [1, 2, 3];
// arr2 : ['a', 'b', 'c'];
// new_arr : [1, 2, 3, 'a', 'b', 'c'];

 정확히 말하면 두 배열의 모든 요소를 복사한 새로운 배열을 만들고, 기존 배열의 내용은 바뀌지 않는다. 그리고 concat은 세 개 이상의 배열을 연결한 새로운 배열을 만들 수도 있다.

 하지만 굳이 원래 배열들의 형태를 유지할 필요가 없고, 새로운 배열이 선언되는 낭비를 줄이고 싶다면 push와 apply를 이용해서 배열을 연결할 수 있다.

arr1.push.apply(arr1, arr2);

// arr1 : [1, 2, 3, 'a', 'b', 'c'];
// arr2 : ['a', 'b', 'c'];

 push 메서드는 두 개 이상의 인자를 넘겨 배열의 맨 뒤에 붙일 수 있다.

arr1.push(4, 5);

// arr1 : [1, 2, 3, 4, 5];

 apply 메서드는 수신자 함수에서 참조하게 될 this값을 첫 번째 인자로 넘기고, 수신자 함수로 넘어갈 인자들을 배열 형태로 넘길 수 있다.

arr1.push.apply(arr1, arr2)

// 아래와 동일한 방법이다.
arr1.push('a', 'b', 'c');


 

'JavaScript' 카테고리의 다른 글

[JavaScript] Trailing Comma 번역자료  (0) 2017.10.02
[JavaScript] 변수 스코프  (0) 2017.04.13
[JavaScript] 생성자 함수  (0) 2017.02.27
[JavaScript] 동등, 일치 연산자  (0) 2016.10.04

Nginx + Gunicorn + Django + PostgreSQL

여태 Django의 빌트-인 웹 서버를 이용하다가, Nginx와 Gunicorn을 연동해보기로 했다. 아래의 링크에서 가장 많은 도움을 받았고, 설치하며 겪었던 시행착오를 정리하기 위해서 글로 정리한다.

환경

  • OS : Ubuntu 16.04
  • Python : 3.4
  • Django : 1.8.5 (LTS)

패키지 설치

$ sudo apt-get update

$ sudo apt-get install python3-pip python3-dev libpq-dev postgresql postgresql-contrib nginx

PostgreSQL

PostgreSQL은 오픈소스 RDBMS다. MySQL에 비해 SQL 표준을 잘 지키고 있고, Genetic query optimizer라고 불리는 알고리즘 덕분에 쿼리가 복잡해질 수록 성능이 더 잘나오는 편이라고 한다.

$ sudo -u postgres psql

  • psql : 쿼리문을 입력할 수 있는 데이터베이스 콘솔 실행.
  • postgres : PostgreSQL의 디폴트 사용자.

postgres=# CREATE DATABASE myproject;

postgres=# CREATE USER myprojectuser WITH PASSWORD 'password';

postgres=# ALTER ROLE myprojectuser SET client_encoding TO 'utf8';

postgres=# ALTER ROLE myprojectuser SET default_transaction_isolation TO 'read committed';

postgers=# ALTER ROLE myprojectuser SET timezone TO 'UTC';

postgres=# GRANT ALL PRIVILEGES ON DATABASE myproject TO myprojectuser;

virtualenv, virtualenv-wrapper

virtualenv는 프로젝트 개발에 필요한 파이썬 패키지를 독립된 가상환경에서 설치하고 관리할 수 있게 돕는 툴인데, virtualenv-wrapper는 기존의 virtualenv를 좀 더 편하게 쓸 수 있도록 몇 가지 기능을 추가한 툴이다.

$ pip3 install virtualenvwrapper

아래의 환경 변수와 쉘 스크립트를 .bash_rc에 추가한다.

.bash_rc

WORKON_HOME=/home/ubuntu/Envs
VIRTUALENVWRAPPER_PYTHON=/usr/bin/python3
source /usr/local/bin/virtualenvwrapper.sh
  • WORKON_HOME이 가르키는 디렉토리에 가상 환경들이 모인다.

$ source ~/.bash_rc : 변경된 .barh_rc 스크립트 실행

$ mkvirtualenv myenv : 가상환경 생성, 활성화

$ deactivate : 가상환경 비활성화

$ workon myenv : 가상환경 활성화

Django

$ workon myenv

$ pip3 install django

$ django-admin.py startproject myproject

$ cd myproject && ./manage.py migrate

settings.py

ALLOWED_HOSTS = ['server_domain_or_ip', ]

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.postgresql_psycopg2',
        'NAME': 'myproject',
        'USER': 'myprojectuser',
        'PASSWORD': '',
        'HOST': 'localhost',
        'PORT': '',
    }
}

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static_files/')
MEDIA_URL = '/uploads/'
MEDIA_ROOT = 'upload_files'

$ ./manage.py collectstatic : 프로젝트 내의 static file(css, javascript, image ..)을 STATIC_ROOT가 가르키는 디렉토리 밑으로 복사

Gunicorn

$ pip3 install gunicorn

/etc/systemd/system/gunicorn.service

[Unit]
Description=gunicorn daemon
After=network.target

[Service]
Environment="variable=value"
User=ubuntu
Group=www-data
WorkingDirectory=/home/ubuntu/myproject
ExecStart=/home/ubuntu/Envs/myenv/bin/gunicorn --workers 3 --bind unix:/home/ubuntu/myproject/myproject.sock myproject.wsgi:application

[Install]
WantedBy=multi-user.target

Environment : .bash_rc에 있는 환경 변수를 읽어오지 못하기 때문에 필요한 시스템 환경 변수는 여기에 작성.

User : gunicorn을 실행할 시스템 사용자의 이름

WorkingDirectory : Django 프로젝트의 경로

ExecStart : django 서버를 실행하는 명령어. ' myproject.sock ' 파일로 Nginx와 통신하게 된다.

$ sudo systemctl start gunicorn

$ sudo systemctl enable gunicorn

$ sudo systemctl daemon-reload : gunicorn.service 파일이 변경됐을 때 적용.

$ sudo systemctl status gunicorn : gunicorn의 실행 상태, 에러 여부도 알 수 있다.

NginX

/etc/nginx/site-available/default

server {
    listen 80;
    server_name my_domain.com;

    location = /favicon.ico { access_log off; log_not_found off; }
    location /static/ {
        alias /home/ubuntu/myproject/static_files/;
    }

    location /uploads/ {
        alias /home/ubuntu/myproject/upload_files/;
    }

    location / {
        include proxy_params;
        proxy_pass http://unix:/home/ubuntu/myproject/myproject.sock;
    }
}

$ sudo ln -s /etc/nginx/site-available/default /etc/nginx/site-enabled/default

 - 'site-enabled' 폴더의 심볼릭 링크('site-available' 폴더의 default 파일)를 기준으로 Nginx 서버가 실행된다.

$ sudo nginx -t : Nginx 설정 파일의 문법 에러 검출.

$ sudo systemctl start nginx : Nginx 서버 재시작.

  • /var/log/nginx/error.log : 에러 로그 파일.

성능비교 ( Pingdom )


Django를 "빌트인 서버"로 실행할 때와 "Nginx+Gunicorn"로 서버를 실행했을 때, 웹 사이트 성능 비교


빌트인 서버

 


Nginx, Gunicorn

 






+ Recent posts