프로그래머스 - [스택]쇠막대기

프로그래머스 문제링크

여러 개의 쇠막대기를 레이저로 절단하려고 합니다. 효율적인 작업을 위해서 쇠막대기를 아래에서 위로 겹쳐 놓고, 레이저를 위에서 수직으로 발사하여 쇠막대기들을 자릅니다. 쇠막대기와 레이저의 배치는 다음 조건을 만족합니다.

  • 쇠막대기는 자신보다 긴 쇠막대기 위에만 놓일 수 있습니다.
  • 쇠막대기를 다른 쇠막대기 위에 놓는 경우 완전히 포함되도록 놓되, 끝점은 겹치지 않도록 놓습니다.
  • 각 쇠막대기를 자르는 레이저는 적어도 하나 존재합니다.
  • 레이저는 어떤 쇠막대기의 양 끝점과도 겹치지 않습니다.
    아래 그림은 위 조건을 만족하는 예를 보여줍니다. 수평으로 그려진 굵은 실선은 쇠막대기이고, 점은 레이저의 위치, 수직으로 그려진 점선 화살표는 레이저의 발사 방향입니다.
    출처: 프로그래머스 코딩테스트 연습 > 스택/큐 >쇠막대기

이러한 레이저와 쇠막대기의 배치는 다음과 같이 괄호를 이용하여 왼쪽부터 순서대로 표현할 수 있습니다.

  • (a) 레이저는 여는 괄호와 닫는 괄호의 인접한 쌍 ‘()’으로 표현합니다. 또한 모든 ‘()’는 반드시 레이저를 표현합니다.
  • (b) 쇠막대기의 왼쪽 끝은 여는 괄호 ‘(‘로, 오른쪽 끝은 닫힌 괄호 ‘)’로 표현됩니다.
    위 예의 괄호 표현은 그림 위에 주어져 있습니다.
    쇠막대기는 레이저에 의해 몇 개의 조각으로 잘리는데, 위 예에서 가장 위에 있는 두 개의 쇠막대기는 각각 3 개와 2 개의 조각으로 잘리고, 이와 같은 방식으로 주어진 쇠막대기들은 총 17 개의 조각으로 잘립니다.

쇠막대기와 레이저의 배치를 표현한 문자열 arrangement 가 매개변수로 주어질 때, 잘린 쇠막대기 조각의 총 개수를 return 하도록 solution 함수를 작성해주세요.

  • 제한사항
    • arrangement 의 길이는 최대 100,000 입니다.
    • arrangement 의 여는 괄호와 닫는 괄호는 항상 쌍을 이룹니다.
  • 입출력 예
    • arrangement: ()(((()())(())()))(())
    • return: 17

나의 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function solution(arrangement) {
let stack = [];
const answer = arrangement.split("").reduce((acc, item, index, arr) => {
if (item === "(" && arr[index + 1] !== ")") {
stack.push(1);
return acc;
} else if (item === ")" && arr[index - 1] !== "(") {
stack.pop();
return acc + 1;
} else {
if (item === ")") {
return acc + stack.length;
}
return acc;
}
}, 0);
return answer;
}

처음에 map을 이용해 탐색하면서 answer변수에 잘린 막대기 수를 담으려다가 그럴거면 reduce를 쓰면 될 것 같아서 수정했다.
괄호가 막대기인지 레이저인지를 먼저 구분했다.
막대기일 경우

  • 막대기 앞 부분(( 여는 괄호)일 경우 stack이라는 배열에 push를 한다.
  • 막대기 뒤 부분() 닫는 괄호)일 경우 stack이라는 배열에서 pop하고, 누적(acc)에 1 을 추가한다.

무엇을 push하냐는 상관없을 것 같다. 레이저일때 스택에 쌓인 수를 얻어오는 부분도 reduce로 처리할까 생각해서 숫자 1 을 넣기는 했는데, length속성으로 구해오면 될 것 같다.

레이저일 경우는

  • 여는 괄호인데 다음 인덱스 아이템이 닫는 괄호인 경우
  • 닫는 괄호인데 이전 인덱스 아이템이 여는 괄호인 경우

이다. 레이저일 때는 push(), pop()을 하지 않으며, 레이저의 닫는 괄호를 만날 때 누적(acc)에 현재 쌓인 stack의 개수(length)를 추가했다.

다른 사람 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function solution(arrangement) {
let answer = 0,
stack = [];
arrangement = arrangement.replace(/\(\)/g, 0);

for (let i = 0; i < arrangement.length; i++) {
switch (arrangement[i]) {
case "(":
stack.push(0);
break;
case "0":
stack = stack.map(v => v + 1);
break;
case ")":
answer += stack[stack.length - 1] + 1;
stack.pop();
break;
}
}
return answer;
}

레이저를 replace를 통해 0 으로 만들어 주고 switch 문을 쓰니까 조건 부분은 훨씬 깔끔해보인다.

그래서 내 풀이도 아래와 같이 수정해봤다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function solution(arrangement) {
let stack = [];
const answer = arrangement
.replace(/\(\)/g, ".")
.split("")
.reduce((acc, item, index, arr) => {
if (item === ".") {
return acc + stack.length;
} else if (item === "(") {
stack.push(1);
return acc;
} else {
stack.pop();
return acc + 1;
}
}, 0);
return answer;
}
Share Comments

프로그래머스 - [해시]완주하지 못한 선수

프로그래머스 문제링크

수많은 마라톤 선수들이 마라톤에 참여하였습니다. 단 한 명의 선수를 제외하고는 모든 선수가 마라톤을 완주하였습니다.
마라톤에 참여한 선수들의 이름이 담긴 배열 participant와 완주한 선수들의 이름이 담긴 배열 completion이 주어질 때, 완주하지 못한 선수의 이름을 return 하도록 solution 함수를 작성해주세요.

  • 제한사항

    • 마라톤 경기에 참여한 선수의 수는 1명 이상 100,000명 이하입니다.
    • completion의 길이는 participant의 길이보다 1 작습니다.
    • 참가자의 이름은 1개 이상 20개 이하의 알파벳 소문자로 이루어져 있습니다.
    • 참가자 중에는 동명이인이 있을 수 있습니다.
  • 입출력 예

participant completion return
[“leo”, “kiki”, “eden”] [“eden”, “kiki”] “leo”
[“marina”, “josipa”, “nikola”, “vinko”, “filipa”] [“josipa”, “filipa”, “marina”, “nikola”] “vinko”
[“mislav”, “stanko”, “mislav”, “ana”] [“stanko”, “ana”, “mislav”] “mislav”
  • 입출력 예 설명
    • 예제 #1
      “leo”는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.
    • 예제 #2
      “vinko”는 참여자 명단에는 있지만, 완주자 명단에는 없기 때문에 완주하지 못했습니다.
    • 예제 #3
      “mislav”는 참여자 명단에는 두 명이 있지만, 완주자 명단에는 한 명밖에 없기 때문에 한명은 완주하지 못했습니다.

나의 풀이

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
function solution(participant, completion) {
let completeMap = new Map();
for(const person of completion) {
const mapItem = completeMap.get(person);
completeMap.set(person, mapItem ? mapItem + 1 : 1);
}
for(const person of participant) {
const mapItem = completeMap.get(person);
if(!mapItem) {
return person;
} else {
completeMap.set(person, mapItem - 1)
}
}
}

문제의 소분류가 해시이니 만큼 해시 테이블을 이용해 풀어보고 싶었다. 다음의 풀이에서 ES6의 Map객체 대신 일반적인 객체로 객체 리터럴과 속성 접근자를 사용해도 문제가 없으나 Map객체를 써보고 싶었다.

먼저 완주자 배열을 순회해 다음 형태의 completeMap을 만든다. 키는 완주자 이름, 값은 완주자 이름이 동명이인일 것을 고려해서 수로 저장한다.

1
2
3
4
5
{
"stanko": 1,
"ana": 1,
"mislav": 1
}

REPL에서 Map을 콘솔에 출력하면 다음과 같이 보여준다.

1
Map(3) {"stanko" => 1, "ana" => 1, "mislav" => 1}

그 다음 참가자 배열을 순회해 해당 참가자 이름으로 completeMap을 탐색해 키가 있다면 저장된 값에 1을 뺀다. 만약 해당 키가 없거나, 저장된 값이 0이면 그 참가자를 반환한다.

다른 사람 풀이

위 방법으로 풀고 나서 다른 사람들 풀이를 보니까 다음과 같이 sort()메서드를 이용해 participant배열과 completion배열을 각각 정렬한 뒤(기본 정렬 순서는 유니코드 코드 포인트 기준이다.) 두 정렬된 배열을 (선형으로) 비교하는 식으로 푼 풀이도 있었다.

1
2
3
4
5
6
7
8
function solution(participant, completion) {
participant.sort();
completion.sort();

for(let i in participant) {
if(participant[i] !== completion[i]) return participant[i];
}
}

요즘 책이랑 영상 강좌만 자꾸 보니까 알고리즘 문제를 풀고 싶어졌다. 적당히 자료구조에 대해서도 생각해볼 수 있고, 어렵지 않고 기분 전환하기 좋은 문제였다.

Share Comments

KAKAO BLIND RECRUITMENT 3차 - 파일명 정렬

프로그래머스 문제링크

카카오 블라인드 테스트 엄청 어렵다. 그나마 이 문제가 그 중에서 쉬운 문제였을 것 같은데,
배열의 sort메소드로 풀다가 sort 메소드가 안전 정렬(stable sorting)이 아니라는 것이 기억나서 해당 메소드를 안전 정렬로 만들어 주는 방법을 찾았다.
해당 방법을 참고해서 겨우겨우 통과하긴 했는데, sort를 어떻게 안정적으로 만드냐를 알게 된 것에 의의를 둬야겠다.

자바스크립트에서 배열의 sort메소드에 인자로 전달되는 비교 함수(compareFunction(a, b))이 반환하는 값이

  • 0보다 작으면 a가 먼저 온다.(a가 b보다 낮은 색인 정렬)
  • 0보다 크면 b가 먼저 온다.
  • 0이면 a와 b를 변경하지 않고 모든 다른 요소에 대해 정렬하는데, 이때 ECMAscript 표준은 이러한 동작을 보장하지 않는다고 한다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function solution(files) {
let answerWrap = files.map((file, index) => ({file, index}));
const compare = (a, b) => {
const reg = /(\D*)(\d*)/i;
const A = a.match(reg);
const B = b.match(reg);
const compareHead = A[1].toLowerCase().localeCompare(B[1].toLowerCase());
const compareNumber = (a, b) => {
return parseInt(a) > parseInt(b) ?
1 : parseInt(a) < parseInt(b) ?
-1
: 0
}
return compareHead === 0 ? compareNumber(A[2], B[2]) : compareHead
}
answerWrap.sort((a, b) => {
const result = compare(a.file, b.file);
return result === 0 ? a.index - b.index : result;
})
return answerWrap.map(answer => answer.file);
}

해당 방법은 map 메소드를 이용해 요소와 인덱스 정보를 가진 객체를 요소로 가지는 배열을 만들어서
비교함수에서는 요소간 비교하고, 반환되는 값이 0일때는 인덱스 정보를 이용해 원래 위치를 비교해 정확한 순서를 얻는다.

Share Comments

TypeScript 사용할 때 'Cannot find module ...' 에러

웹팩과 타입스크립트를 공부하면서 자동판매기 프로젝트를 진행하다가 문제가 발생했다.
처음에 loader등의 웹팩 설정에서 뭘 잘못한 줄 알았다. 결론적으로 문제는 tsconfig.js파일을 수정해 해결했다.

node_modules로 설치한 다른 라이브러리 모듈을 찾지 못하는 문제였는데, 예를 들어 npm install --save redux로 리덕스를 설치하고

1
2
// app.ts
import { CreateStore } from 'redux'

위와 같이 불러오려고 했는데 모듈을 찾지 못한다고 에러가 발생했다.
(참고로 @types/redux는 더이상 사용되지 않는다.)

1
2
ERROR in [at-loader] ./src/app.ts:1:16
TS2307: Cannot find module 'redux'.

‘ts-loader’를 사용했을 때는 VS Code의 문제(PROBLEMS)탭에서 'redux' 모듈을 찾지 못했습니다.라고 나왔고, ‘awesome-typescript-loader’를 사용했을 때는 터미널 탭에서 위와 같이 웹팩이 실행되면서 Cannot find module 'redux'라고 나왔는데, 이건 환경에 따라 다를 수도 있을 것 같다.

문제가 발생했을 때의 tsconfig.js는 다음과 같았는데

1
2
3
4
5
6
7
8
9
10
{
"compilerOptions": {
"outDir": "./dist",
"noImplicitAny": true,
"module": "ES6",
"target": "ES5",
"allowJs": true,
"sourceMap": true,
}
}

포스팅 하단에 적어둔 참고 링크들에서 다음과 같은 해결방법을 찾았다. 다음과 같이 "moduleResolution": "node"를 추가했더니 문제가 해결되었다.

1
2
3
4
5
6
7
8
9
10
11
{
"compilerOptions": {
"outDir": "./dist",
"noImplicitAny": true,
"module": "ES6",
"target": "ES5",
//+ "moduleResolution": "node",
"allowJs": true,
"sourceMap": true,
}
}

위 옵션을 추가해도 해결되지 않는다면, 나의 경우처럼 VS Code를 종료하고 다시 열어야 해결될 수도 있다.

moduleResolution옵션의 기본값은 타입스크립트 공식문서에서 다음과 같이 쓰여있다.

1
module === "AMD" or "System" or "ES6" ? "Classic" : "Node"

module 옵션이 AMD, System, ES6(혹은 ES2015)일 경우에 기본값은 Classic이고 나머지는 Node이다.

타입스크립트 공식문서의 Module Resolution항목에 모듈 분석 전략 두 가지 방법인 ClassicNode에 대한 설명이 나와있다.

간단하게 정리하면 import { b } from "moduleB"와 같이 상대 경로가 아닌 모듈(non-relative module) 불러오기에서 Classic과 달리 Node 설정은 ‘node_modules’폴더를 찾아 모듈을 검색한다.

/root/src/A.ts파일에 아래과 같이 상대 경로가 아닌 모듈(non-relative module) 불러오기를 했다고 하면

1
2
// /root/src/A.ts
import { b } from "moduleB"

Classic 설정은 다음과 같은 순서로 moduleB를 찾는다.

  1. /root/src/moduleB.ts
  2. /root/src/moduleB.d.ts
  3. /root/moduleB.ts
  4. /root/moduleB.d.ts
  5. /moduleB.ts
  6. /moduleB.d.ts

Node 설정은 다음과 같이 ‘node_modules’폴더를 찾아 해당 모듈을 검색한다.

  1. /root/src/node_modules/moduleB.ts
  2. /root/src/node_modules/moduleB.tsx
  3. /root/src/node_modules/moduleB.d.ts
  4. /root/src/node_modules/moduleB/package.json (if it specifies a “types” property)
  5. /root/src/node_modules/moduleB/index.ts
  6. /root/src/node_modules/moduleB/index.tsx
  7. /root/src/node_modules/moduleB/index.d.ts
  8. /root/node_modules/moduleB.ts
  9. /root/node_modules/moduleB.tsx
  10. /root/node_modules/moduleB.d.ts
  11. /root/node_modules/moduleB/package.json (if it specifies a “types” property)
  12. /root/node_modules/moduleB/index.ts
  13. /root/node_modules/moduleB/index.tsx
  14. /root/node_modules/moduleB/index.d.ts
  15. /node_modules/moduleB.ts
  16. /node_modules/moduleB.tsx
  17. /node_modules/moduleB.d.ts
  18. /node_modules/moduleB/package.json (if it specifies a “types” property)
  19. /node_modules/moduleB/index.ts
  20. /node_modules/moduleB/index.tsx
  21. /node_modules/moduleB/index.d.ts

참고링크

Share Comments

TIL

2018.08.18

  • 리덕스를 적용한 vending machine 프로젝트를 진행하기로 함

2018.08.19

2018.08.20

  • 리액트 튜토리얼 틱택토(타임트래블), 틱택토 리덕스 연결
  • 밴딩 머신 리액트로 만들어보기 이전에 바닐라 자바스크립트로 먼저 구현해보는 게 나을 것 같아서 프로젝트를 새로 만듦
    • Parcel을 이용할 지, webpack을 이용할 지 고민하다가 Webpack을 공부해볼겸 Webpack을 적용하기로 함
    • babel, sass를 위한 로더를 설정함
  • Chrome Dev Meetup by GDG Korea WebTech 참여
    • Session A - Progressive Web Apps
    • Session B - CSS Scroll Snap in 69

2018.08.21

  • 하이어링 데이 준비
    • 스쿨 사이트에 올린 이력서 업데이트(사이트의 텍스트 에디터가 날 괴롭게 해… 리스트 형식 스타일이 왜 그렇죠??)
  • 포트폴리오를 요구하는 곳들도 있어서 문서화하다가 디자인 때문에 괴로워짐
  • 포트폴리오 사이트를 만들자는 생각에 포트폴리오 프로젝트 시작
    • svg나 three.js 써서 화려하게 만들고 싶지만, 나중에 공부하면서 만들기로 하고 바닐라 자바스크립트 복습할 겸 밴딩 머신때 만든 웹팩 설정 가져다 메인 페이지 만들어 봤다.

2018.08.22

  • [Meetup with Googler] introduction to AMP stories 참여
    • AMP 스토리 포맷에 대한 영상과 설명을 들으면서 20일 밋업 두번째 세션에서 점점 ‘구글같은 기업에서 다 만들어주고 우리는 가져다 선언식으로 쓰면되는 선언형 언어로 간다’는 말이 다시 생각났다.
    • 애니메이션 효과가 들어가는 네이버 웹툰도 생각났다.
    • AMP 스토리 포맷
Share Comments

TIL

2018.08.12

  • 타입스크립트 인터페이스 정리

2018.08.13

  • 영어학원
  • 타입스크립트 인터페이스 (함수인터페이스, 클래스 인터페이스)

2018.08.14

  • 리덕스 카운터 예제
  • 누구나 자료구조와 알고리즘 책 1 ~ 4장
    • 배열, 집합 - 이진검색/선형검색
    • 빅오 표기법
    • 버블정렬

2018.08.15

  • 리덕스 심화 예제
  • 누구나 자료구조와 알고리즘 책 5 ~ 6장
    • 선택정렬, 삽입정렬

2018.08.16

  • 리덕스
    • 리덕스 API
    • 리덕스 심화 예제
    • 리덕스 미들웨어 실습
  • 누구나 자료구조와 알고리즘 책 7 ~ 8장
    • 해시 테이블
    • 스택, 큐
Share Comments

TIL

블록체인 하이어링 면접

패스트캠퍼스에서 진행한 블록체인 하이어링 행사를 다녀왔다. 블록체인 개발자뿐만이 아니라 프론트엔드 개발자도 뽑고 있으니 지원해보라는 학원 측 제의에 이력서를 전달했고 면접일정이 잡혀서 보러간 거였는데, 블록체인 위주의 면접이 되어 당황스러웠다. 프론트엔드 개발자로써의 역량이나 목표보다는 블록체인에 대해 어느정도 구체적인 청사진을 그리고 있는 사람을 원하는 것 같았고, 블록체인에 대해 구체적인 방향성을 좀 더 가지는 게 좋을 거라는 조언을 들었다. 면접보다는 상담하는 자리가 되어버려 아쉽지만, 그래도 개인적으로 블록체인에 대해 잘 이해가 안되었던 걸 현업자에게 질문할 수 있는 자리가 되었고, 앞으로의 면접에 필요한 자세를 조언받아 도움이 되었다.
블록체인 서비스를 제공하면서 어떻게 수익을 낼 수 있는지나, 과연 어떤 서비스에 적용할 수 있는지, 속도에 관한 것에 대해 어떻게 생각하시는 지 여쭤봤는데 아직 블록체인 쪽은 많은 시행착오를 거치는 중이라는 느낌을 많이 받았고 또 다른 의문도 많이 생겨나서 오늘을 계기로 더 관심가지고 공부를 해봐야겠다.

크립토좀비

크립토좀비는 이더리움 상의 DApp 코딩과 solidity를 좀비 게임만들기를 통해 학습할 수 있도록하는 튜토리얼 사이트이다. 한글도 지원한다.


어제 기술면접도 어느정도 고려해서 복습을 하다보니 내가 아직 많이 부족하다는 걸 알았다. 계속 이론만 공부하는 것보단 현장에서 일을 하면서 배우는 것이 더욱 도움이 되겠지만, 구직하면서 더 열심히 공부해야겠구나 생각이 들었다.
챗봇 책 사둔것을 참고해서 토이프로젝트를 하려는데, 좀 충격을 받은 게 나는 분명 초판이 올해 6월이길래 이정도면 최신 정보일거라고 예상하고 샀다. 그러나 책에서 다루는 폴리머는 2.x 버전이고 현재는 3.0버전이 나왔다. 버전 앞의 수가 올라간만큼 큰 변화가 있었는데, html파일에서 작업되고, import도 <link rel=import href="foo.html">를 이용했던 방식에서 js파일 내에서 ES6 모듈 import를 사용하는 방식으로 변한 게 가장 컸다.
만약 당장 폴리머를 쓰는 회사를 가게 된다면, 2.x 버전을 유지보수할 가능성이 크겠지만. 6월 발간 책이 금방 지난 기능을 담게 될 정도로 프론트엔드의 변화가 굉장히 빠르구나를 새삼 느꼈다.

Share Comments

TIL

오늘 한 일

타입스크립트 공부

연결리스트를 만들면서 타입스크립트를 함께 써봤다. 만들면서 필요한 부분을 공식문서나 다른 글에서 찾아서 쓰는 식으로 써봤는데, Object타입은 내 예상과 다르게 동작했고, 문서를 읽어보니 실제로도 내가 원하는 동작을 하도록 하려면 유용하지 않았다. 그리고 Array<number>같은 표기도 등장한다. jest랑 같이 써보면서 느낀 점은 아직 둘 다 잘 알고 사용하는 건 아니라서 제대로 쓴 것 같지는 않지만, 확실히 내 코드가 나의 예상과 다른게 동작하고 있지 않은지 체크하는데 굉장히 유용했다. 그런데 테스트 코드 작성하는 것도 만드는 것 못지 않게 시간이 든다…

타입스크립트는 공식문서와 『자바스크립트 개발자를 위한 타입스크립트』라는 안희종 개발자님이 Gitbook으로 연재한 글을 참고하고 있다.

ECMASciprt 파트는 대부분 아는 내용이라 빠르게 훑어봤는데, 내가 호이스팅에 대해서 잘못 알고 있던 부분이 있었다. ‘호이스팅은 var를 쓸 경우에만 일어난다’고 알고있었는데 ‘temporal-dead-zone’이라는 개념이 있었다. 그거랑 분해 대입(비구조화 할당)를 따로 정리하면 좋을 것 같다.

Share Comments

TIL

오늘 한 일

블로그 검색엔진 최적화

대부분의 글이 알고리즘 문제 풀이나 TIL뿐이어서 블로그 검색엔진 최적화를 계속 제일 나중에 할 일로 미루다가 오늘 했다.
hexo 블로그의 검색엔진 최적화 방법을 엄청 잘 정리해주신 글이 있어서 참고했다.

Jest + Typescript + 연결리스트

Jest로 테스트하는 걸 연습하면서 타입스크립트를 써보면서 연결리스트를 구현하고 있다. 연결리스트를 좀 잘못 생각하고 있던 게 있어서 다시 짜면서 만들어보고 있다.

Share Comments

TIL

오늘 한 일

You don’t know JS: 값 정리중

내용은 어렵지 않은데, ‘슬롯’이라는 용어를 처음 봤다. 슬롯이 뭐지? 슬롯 머신??? 그런데 딱히 ‘array slot’, ‘javascript slot’ 뭐 이런 키워드로 검색해도 나오지 않는 것 보면 어떤 정식 명칭 같은 것은 아니고 그냥 슬롯 머신의 그 슬롯처럼 영어 뜻 그대로 ‘무언가 들어갈 자리, 구멍’의 의미로 쓴 것 같다.

그리고 검색하다가 나온건데 웹컴포넌트 관련해서 <slot>태그가 있다.

Jest와 타입스크립트 같이 쓰는 환경 구성

이왕 Jest도 공부하면서 타입스크립트로 같이 공부하자는 생각에 일단 환경을 설정했다.

참고한 글

구입한 책

(의도한 것은 아니지만 출판사가 다른데도 책 제목이 너무나 쉽게 가려는 나의 마음을 반영하는 것 같군…)
서점 들린김에 읽어보다가 마음에 들어서 구입했다. 특히 챗봇은 취향저격이라. 공부하고 싶었던 것들이 한번에…(오오 폴리머!! 파이어베이스!!! 슬랙봇!!!) 책 따라서 만들어보고 리액트로도 도전해보는 걸 목표로 해야겠다. 나도 슬랙봇 만들어야지!!

Share Comments