세로 길이 2, 가로 길이 n인 2 x n 보드가 있습니다. 2 x 1 크기의 타일을 가지고 이 보드를 채우는 모든 경우의 수를 리턴해야 합니다.
입력
인자 1 : n
number 타입의 1 이상의 자연수
출력
number 타입을 리턴해야 합니다.
주의사항
타일을 가로, 세로 어느 방향으로 놓아도 상관없습니다. (입출력 예시 참고)
입출력 예시
let output = tiling(2);
console.log(output); // --> 2
output = tiling(4);
console.log(output); // --> 5
/*
2 x 4 보드에 타일을 놓는 방법은 5가지
각 타일을 a, b, c, d로 구분
2 | a b c d
1 | a b c d
------------
2 | a a c c
1 | b b d d
------------
2 | a b c c
1 | a b d d
------------
2 | a a c d
1 | b b c d
------------
2 | a b b d
1 | a c c d
------------
*/
Advanced
타일링 문제를 해결하는 효율적인 알고리즘(O(N))이 존재합니다. 반드시 직접 문제를 해결하시면서 입력의 크기에 따라 어떻게 달라지는지 혹은 어떻게 반복되는지 관찰하시기 바랍니다.
// naive solution: O(2^N)
// 2 x 4 보드에 타일을 놓는 방법은 5가지다.
// 각 타일을 a, b, c, d로 구분한다.
// 아직 타일이 놓이지 않는 부분은 -로 표기한다.
// 타일을 놓는 방법은 가장 왼쪽부터 세로로 놓거나 가로로 놓는 것으로 시작한다.
// 1) 세로로 놓는 법
// 2 | a - - -
// 1 | a - - -
// ------------
// 2) 가로로 놓는 법
// 타일을 가로로 놓게 되면, 그 바로 아래에는 가로로 놓을 수 밖에 없다.
// 2 | a a - -
// 1 | b b - -
// ------------
// 이때, 타일이 아직 놓이지 않은 부분은 사실 크기만 다를뿐 같은 종류의 문제라는 것을 알 수 있다.
// 즉, 2 x 4 보드에 타일을 놓는 방법은 아래 두 가지 방법을 더한 결과와 같다.
// 1) 2 x 3 보드에 타일을 놓는 방법
// 2) 2 x 2 보드에 타일을 놓는 방법
// 따라서 2 x n 타일 문제는 아래와 같이 재귀적으로 정의할 수 있다.
// 주의: 재귀적 정의에는 항상 기초(base), 즉 더 이상 재귀적으로 정의할 수 없는(쪼갤 수 없는) 문제를 별도로 정의해야 한다.
// let tiling = function (n) {
// if (n <= 2) return n;
// return tiling(n - 1) + tiling(n - 2);
// };
// dynamic with memoization: O(N)
let tiling = function (n) {
// 인덱스를 직관적으로 관리하기 위해
// 앞 부분을 의미없는 데이터(dummy)로 채운다.
const memo = [0, 1, 2];
// 재귀를 위한 보조 함수(auxiliary function)을 선언)
const aux = (size) => {
// 이미 해결한 문제는 풀지 않는다.
if (memo[size] !== undefined) return memo[size];
if (size <= 2) return memo[n];
memo[size] = aux(size - 1) + aux(size - 2);
return memo[size];
};
return aux(n);
};
// dynamic with tabulation: O(N)
// tabulation은 데이터를 테이블에 정리하면서 bottom-up 방식으로 해결하는 기법을 말합니다.
// let tiling = function (n) {
// const memo = [0, 1, 2];
// if (n <= 2) return memo[n];
// for (let size = 3; size <= n; size++) {
// memo[size] = memo[size - 1] + memo[size - 2];
// }
// return memo[n];
// };
// dynamic with slicing window: O(N)
// slicing window은 필요한 최소한의 데이터만을 활용하는 것을 말합니다.
// 크기 n의 문제에 대한 해결을 위해 필요한 데이터는 오직 2개뿐이라는 사실을 이용합니다.
// let tiling = function (n) {
// let fst = 1,
// snd = 2;
// if (n <= 2) return n;
// for (let size = 3; size <= n; size++) {
// // 앞의 두 수를 더해 다음 수를 구할 수 있다.
// const next = fst + snd;
// // 다음 문제로 넘어가기 위해 필요한 2개의 데이터의 순서를 정리한다.
// fst = snd;
// snd = next;
// }
// return snd;
// };
리눅스 시스템은 기본적으로 multi-user 개념에서 시작하였기 때문에 시스템을 이용하기 위해서는 반드시 로그인을 하여야 합니 다. 로그인은 PC 통신에서도 많이 사용되어져 왔기 때문에 그 개 념 설정에 그다지 어려움이 없을 것입니다. 흔히 말하는 ID를 입력하는 과정입니다.
passwd
패스워드 변경
리눅스, 특히 인터넷의 세계에서는 일반 컴퓨팅 상황에 비하여 훨씬 해킹에 대한 위험이 높습니다. 패스워드는 완성된 단어 보다는 단어 중간에 숫자나 키보드의 ^, #, ' 등과 같은 쉽게 연상 할 수 없는 기호를 삽입하여 만들어 주는 것이 좋습니다
du
하드사용량 체크(chkdsk)
자신의 하드공간을 알려면 # du 특정 디렉토리의 사용량을 알려면 # du -s diretory_name
ls
파일 리스트 보기(dir)
F : 파일 유형을 나타내는 기호를 파일명 끝에 표시 (디렉토리는 '/', 실행파일은 '*', 심볼릭 링크는 '@'가 나타남). l : 파일에 관한 상세 정보를 나타냅니다. a : dot 파일(.access 등)을 포함한 모든 파일 표시. t : 파일이 생성된 시간별로 표시 C : 도스의 dir/w명령과 같 이 한줄에 여러개의 정보를 표시 R : 도스의 dir/s 명령과 같이 서브디렉토리 내용까지.
(예) # ls -al # ls -aC # ls -R
cd
디렉토리를 변경
# cd cgi-bin : 하부 디렉토리인 cgi-bin으로 들어감. # cd .. : 상위디렉토리로 이동 # cd 또는 cd ~ : 어느곳에서든지 자기 홈디렉토리로 바로 이동 # cd /webker : 현재 작업중인 디렉토리의 하위나 상위 디렉토리가 아닌 다른 디렉토리(webker)로 이동하려면 /로 시작해서 경로이름을 입력하면 된다.
# chmod 766 guestbook.html : 자신은 모든 권한을 그룹사용자와,전체사용자에게는 읽기와 쓰기 권한만 줌
alias
" doskey alias" 와 비슷하게 이용할 수 있는 쉘 명령어 alias는 말그대로 별명입니다. 사용자는 alias를 이용하여 긴 유 닉스 명령어를 간단하게 줄여서 사용할 수도 있습니다. 이들 앨리어스는 [alias ls 'ls -al'] 같이 사용하시면 되는데, 한 번 지정한 alias를 계속해서 이용하시려면, 자신의 홈디렉토리에 있는 .cshrc(Hidden 속성)을 pico등의 에디터를 이용하여 변경시 키면 됩니다.
cat
파일의 내용을 화면에 출력하거나 파일을 만드는 명령( 도스의 TYPE명령)
# cat filename
more
cat 명령어는 실행을 시키면 한 화면을 넘기는 파일일 경우 그 내용을 모두 볼수가 없다. 하지만 more 명령어를 사용하면 한 화면 단위로 보여줄 수 있어 유용.
# more <옵션> 옵션은 다음과 같습니다.
Space bar : 다음 페이지 Return(enter) key : 다음 줄 v : vi 편집기로 전환 /str : str 문자를 찾음 b : 이전 페이지 q : more 상태를 빠져나감 h : 도움말 = : 현재 line number를 보여줌
who
현재 시스템에 login 하고 있는 사용자의 리스트를 보여줍니다.
# who
whereis
소스, 실행파일, 메뉴얼 등의 위치를 알려줍니다
# whereis perl : perl의 위치를 알려준다
vi, touch, cat
새로운 파일을 만드는 방법
# vi newfile : vi 편집기 상태로 들어감 # touch newfile : 빈 파일만 생성됨 # cat > newfile : vi 편집기 상태로 들어감, 문서 작성후 Ctrl+D로 빠져나옴
cat, head, tail
파일 내용만 보기
# cat filename : 파일의 내용을 모두 보여줌 # head -n filename : n줄 만큼 위세서부터 보여줌 # tail -n filename : n줄 만큼 아래에서부터 보여줌
'Access-Control-Allow-Origin'의 값은 기본적으로 한개의 origin을 입력받던가 모든 오리진을 허용해주는 '*'이라는 와일드카드를 값으로 받을 수 있다.
만약 여러개의 origin을 허용하고 싶다면 따로 배열을 만들고 들어온 request의 origin이 그 배열에 포함이 되있는지 확인 하는방법을 사용할 수 있다. 확인 후에 해당 origin을 현재 'Access-Control-Allow-Origin'의 값으로 넣어주면 된다.
const server = http.createServer((request, response) => {
let body = [];
let allowedOrigins = [
//multiple origin 설정하기
'http://127.0.0.1:5500',
'http://14.36.99.81:44949',
'http://14.36.99.81:35911',
];
let origin = request.headers.origin; //@@@@@@@@@@@@@@@@@@@@@@@@@@@@origin 가져오기
temp = request.headers;
console.log(
'defaultCorsHeader[Access-Control-Allow-Origin]: ',
defaultCorsHeader['Access-Control-Allow-Origin'],
);
console.log('origin: ', origin);
if (allowedOrigins.includes(origin)) {
defaultCorsHeader['Access-Control-Allow-Origin'] = origin;
}
}
then()의 리턴값은 promise객체이며, 인자로 받은 함수의 리턴값이 promise객체이면 그 객체의 상태와 결과값을 가진다.
보통 then안에
return new Promise((resolve, reject)=>{
if(조건문)resolve(전달할게 있다면 전달)
else reject(전달할게 있다면 전달)
})
이런식으로 사용하여 비동기적 코딩을 실행한다.
리턴값이 일반 데이터면 그 일반데이터를 결과값으로 가지는 promise객체를 반환하지만 비동기적으로 순차적으로 then이 실행되는지 장담하기 힘들다.
Promise.resolve('foo')
// 1. "foo"를 받고 "bar"를 추가한 다음 그 값으로 이행하여 다음 then에 넘겨줌
.then(function(string) {
return new Promise(function(resolve, reject) {
setTimeout(function() {
string += 'bar';
resolve(string);
}, 1);
});
})
// 2. "foobar"를 받고 그대로 다음 then에 넘겨준 뒤,
// 나중에 콜백 함수에서 가공하고 콘솔에 출력
.then(function(string) {
setTimeout(function() {
string += 'baz';
console.log(string);
}, 1)
return string; //여기에서 일반 데이터를 반환하면 문제가 생김@@@@@@
})
// 3. 이 부분의 코드는 이전의 then 블록 안의 (가짜) 비동기 코드에서
// 실제로 문자열을 가공하기 전에 실행됨
.then(function(string) {
console.log("마지막 Then: 앗... 방금 then에서 프로미스 만들고 반환하는 걸 까먹어서 " +
"출력 순서가 좀 이상할지도 몰라요");
// 'baz' 부분은 setTimeout 함수로 비동기적으로 실행되기 때문에
// 이곳의 string에는 아직 'baz' 부분이 없음
console.log(string);
});
// 로그 출력 결과 (순서대로):
// 마지막 Then: 앗... 방금 then에서 프로미스 만들고 반환하는 걸 까먹어서 출력 순서가 좀 이상할지도 몰라요
// foobar
// foobarbaz
await를 사용하면 promise객체를 리턴하는 함수 앞에 키워드를 쓰게되는데,
async function getNewsAndWeatherAsync() {
let result = {}
let data1 = await fetch(newsURL).then(response => response.json())
let data2 = await fetch(weatherURL).then(response => response.json())
result = { news : data1.data, weather : data2}
return result
}
여기서 data1의 값은 promise객체가 아니라 promise객체의 결과값이다!!
Promise.all
var newsURL = 'http://localhost:5000/data/latestNews';
var weatherURL = 'http://localhost:5000/data/weather';
function getNewsAndWeatherAll() {
// TODO: Promise.all을 이용해 작성합니다
return Promise.all([ fetch(newsURL), fetch(weatherURL) ])
.then((responses) => {
return Promise.all(responses.map((response) => response.json()))
})
.then((responses) => {
let result = {}
// console.log(responses[0])
result = { news: responses[0].data, weather: responses[1] }
return result
})
}
if (typeof window === 'undefined') {
module.exports = {
getNewsAndWeatherAll
}
}
promise.all 또한 await와 마찬가지로 위의 responses에 promise객체들의 배열을 인자로 전달해주는것이 아닌 promise객체들의 결과값을 담은 배열을 then에 넘겨준다.
Promise 실행함수가 가지고 있는 두 개의 파라미터, resolve, reject는 각각 무엇을 의미하나요?
resolve는 주어진 값으로 이행하는 Promise객체를 반환합니다. 값이 then가능한 (즉, then메서드가 있는) 경우, 반환된 프로미스는 then메서드를 따라가고 마지막 상태를 취합니다. 그렇지 않은 경우 반환된 프로미스는 주어진 값으로 이행합니다. 어떤 값이 프로미스인지 아닌지 알 수 없는 경우,Promise.resolve(value)후 반환값을 프로미스로 처리할 수 있습니다.
reject는 주어진 이유로 거부하는 Promise객체를 반환합니다.
resolve, reject함수에는 인자를 넘길 수 있습니다. 이때 넘기는 인자는 어떻게 사용할 수 있나요?
때로는 직접 눈으로 확인해보는 것이 이해하기 쉽습니다. 반드시 다음을 실습해보세요.
sleep함수를 아래와 같이 바꿉니다.constsleep=(wait)=>{returnnewPromise((resolve)=>{setTimeout(()=>{resolve('hello')}, wait);});}
runPromise함수를 다음과 같이 바꿉니다.functionrunPromise(){resetTitle();playVideo();sleep(1000).then((param)=>{console.log(param);pauseVideo();displayTitle();return'world'}).then((param)=>{console.log(param);sleep(500);}).then(highlightTitle).then(sleep.bind(null,2000)).then(resetTitle)}
브라우저 콘솔을 확인해서, 어떤 일이 발생했는지 확인해보세요.
new Promise()를 통해 생성한 Promise 인스턴스에는 어떤 메소드가 존재하나요? 각각은 어떤 용도인가요?then() 메소드: then을 호출한 함수에서 반환되는 promise의 인자가 resolve를 호출할때 실행
then(), catch(), finally() 메소드가 존재한다.
catch() 메소드: 이전 then에서 호출한 함수에서 반환되는 promise의 인자가 reject를 호출 할때 실행
finally() 메소드: Promise가성공적으로 수행 되었는지 거절되었는지에 관계없이 Promise가 처리 된 후에 코드가 무조건 한 번은 실행되는 것을 제공합니다.
Promise.prototype.then메소드는 무엇을 리턴하나요?
then 메소드를 호출한 promise객체를 리턴하고, 그 안에는 promise객체에서 호출한 함수의 반환값을 키와 값의 형태로 갖고있다. 또한 현promise객체의 상태에 대한 정보도 갖고있다.
Promise.prototype.catch메소드는 무엇을 리턴하나요?
때로는 직접 눈으로 확인해보는 것이 이해하기 쉽습니다. 반드시 다음을 실습해보세요.
catch를 테스트해보고 싶다면 sleep함수를 다음과 같이 고쳐봅시다.constsleep=(wait)=>{returnnewPromise((resolve, reject)=>{setTimeout(()=>{reject(newError('에러'))}, wait);});}
runPromise함수의 Promise 사용 부분에 catch메소드를 붙여봅니다.// 생략.then(resetTitle).catch(err =>{console.log(err);})
catch 메소드를 호출한 promise객체를 리턴하고, 그 안에는 promise객체에서 호출한 함수의 반환값을 키와 값의 형태로 갖고있다. 또한 현promise객체의 상태에 대한 정보도 갖고있다.
Promise의 세가지 상태는 각각 무엇이며, 어떤 의미를 가지나요?
Pending(대기) : 비동기 처리 로직이 아직 완료되지 않은 상태
Fulfilled(이행) : 비동기 처리가 완료되어 프로미스가 결과 값을 반환해준 상태
Rejected(실패) : 비동기 처리가 실패하거나 오류가 발생한 상태
await키워드 다음에 등장하는 함수 실행은, 어떤 타입을 리턴할 경우에만 의미가 있나요?
promise타입을 리턴하는 경우에만 의미가 있다.
await키워드를 사용할 경우, 어떤 값이 리턴되나요?
때로는 직접 눈으로 확인해보는 것이 이해하기 쉽습니다. 반드시 다음을 실습해보세요.
sleep함수를 아래와 같이 바꿉니다.constsleep=(wait)=>{returnnewPromise((resolve)=>{setTimeout(()=>{resolve('hello')}, wait);});}
브라우저에서 개발자 콘솔을 열어 다음을 실행해본 후, returnValue에 담긴 값을 확인해보세요.let returnValue =awaitsleep(1000);
return 되는 promise객체에 담겨져 있던 [[PromiseResult]]의 값이 담긴다.