수업시간 대부분이 실습 시간이었는데 덕분에 조금 리액트 state와 props, Component같은 기본 사용법은 익숙해지는 것 같다. 빨리 숙지해서 Redux같은 심화 부분을 들을때 어려움이 없도록 많이 연습해야겠다.
2. Algorithm 문제 풀이
저번 풀이는 연결 리스트에 대해 이해가 없어서 오래 걸렸다. 몇번 실행 오류 나보면서 이해하게 되었지만, 나중에 따로 정리해봐야겠다. 다른 식으로 풀어봤는데, 다행히도 저번에 푼 것보다는 효율적인 코드인 것 같다. 시간이 단축되었다. 다른 방법 딱 한번만 더 생각해보고 다른 사람 풀이 보면서 포스팅을 다시 추가해야겠다.
앞으로 할 일
React 캘린더 완성하기
RGB 챌린지 React로 구현해보기
알고리즘 Add Two Numbers 다시 풀어보고 다른 사람 풀이 정리하기(데이터 구조 - 연결리스트 정리하자)
Given a 32-bit signed integer, reverse digits of an integer.
Example 1:
Input: 123
Output: 321
Example 2:
Input: -123
Output: -321
Example 3:
Input: 120
Output: 21
Note: Assume we are dealing with an environment which could only store integers within the 32-bit signed integer range: [−231, 231 − 1]. For the purpose of this problem, assume that your function returns 0 when the reversed integer overflows.
나의 풀이
1 2 3 4 5 6
// Runtime: 80 ms var reverse = function(x) { const range = 2 ** 31; const result = parseInt(x.toString().split('').reverse().join('')); return (result > (range - 1)) ? 0 : x >= 0 ? result : result * -1; };
코드가 짧아보인다고 빠른게 아닌 것임을… Big O Notation을 아직 제대로 이해하지 못했지만 제아무리 코드상 짧아보여도 반드시 좋은 코드인 것은 아니란 걸 알았다.(그리고 나의 코드가 그런듯…)
range = 2 ** 31은 Math.pow(2, 31)로 구해도 될 것 같고 숫자를 문자열 화하는 방법은 다양하다. ('' + x, ${x}, x.toString()…)
문제 자체가 풀기에는(정말 푼 것에만 의미를 두자면) 크게 어렵지 않았고, 나는 숫자를 reverse한다고 해서 당연히 1문자열로 만들어서, 2배열화해서, 3reverse메소드로 뒤집고 다시 4join메소드로 합쳐준 다음 5숫자화하면 되는 것 아닌가?라고 생각했다. 그리고 이게 거의 공식처럼 머릿속에 있었는데, 다른 사람들 풀이를 보니까 생각이 넓어지는 기분이다.
// 64ms var reverse = function(x) { var isNeg = x < 0; var result = 0; x = Math.abs(x); while(x) { var lastDigit = x % 10; // 33 % 10 = 3 result *= 10; // 0 result += lastDigit; // 3 x = parseInt(x / 10); // 3 }
만약 제한범위를 넘어서면([−231, 231 − 1]) 0을 반환하고 아니면 result를 그대로 반환한다.
위의 풀이와 비슷한데 조금 다른 풀이
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
// 68ms var reverse = function(x) { var MAX = Math.pow(2, 31) - 1; var number = 0, symbol = 1;
if(x < 0){ x = Math.abs(x); symbol = -1; }
while(x > 0){ number = number * 10 + x % 10; x = Math.floor(x / 10); }
return number > MAX ? 0 : symbol * number; };
1 2 3 4 5 6 7 8 9 10 11
// 76ms const reverse = function(x) { if (x==0) return0 let isPositive = x > 0 let digits = (isPositive)? x.toString().split("").reverse() : (-x).toString().split("").reverse() let num = parseInt(digits.join(''), 10) if (num >= 2147483647) return0 return num * (isPositive? 1: -1) };
이 풀이는 나의 경우 Math.pow(2, 31)내지는 2 ** 31로 구하도록 했는데 이미 구해진 값을 넣어서 연산을 하나 줄였다.
대체로 while문을 통해서 10을 이용한 연산을 통해 자리수를 바꿔주는 방법이 문자열 → 배열 → reverse로 하는 방법보다 빠른 것 같다.
패스트 캠퍼스 프론트엔드 개발 스쿨에서 배운 내용을 정리합니다. 내용에 오류가 있다면 댓글 남겨주시기 바랍니다.
예외처리
프로그램의 언어들이 항상 완벽하게 동작하지는 않는다. 단순 버그이거나, 프로그래머가 잘못 작성했거나, 프로그래밍 언어 자체가 에러를 내는 경우가 있다.
1. 동기식 코드에서의 예외 처리
프론트엔드 개발자의 실수
1
newArray(-1) // RangeError: Invalid array length
1
console.log(foo) // ReferenceError: foo is not defined
프론트엔드 개발자의 의도와 다른 실수
1 2
fetch('https://nonexistent-domain.nowhere'); // TypeError: Failed to fetch // 위 주소는 맞는데 서버/네트워크 쪽 에러가 있다거나
에러가 발생하면 나머지 로직이 실행되지 않는다. 그 시점에 실행 중이었던 작업을 완료할 수 없게 된다.
1 2 3
console.log('에러가 나기 직전까지의 코드는 잘 실행됩니다.'); newArray(-1); // RangeError: Invalid array length console.log('에러가 난 이후의 코드는 실행되지 않습니다.'); // 이것은 실행되지 않는다.
위와 같이 코드의 실행이 중단된다.
try...catch...finally 구문을 사용하면 에러가 나더라도 코드의 실행을 지속할 수 있다.
1 2 3 4 5 6 7 8 9 10
try { // 여기서 에러가 나면 에러가 난 시점에 코드의 흐름이 // catch로 넘어간다. console.log('에러가 나기 직전까지의 코드는 잘 실행됩니다.'); newArray(-1); // RangeError: Invalid array length console.log('에러가 난 이후의 코드는 실행되지 않습니다.'); } catch (e) { console.log('코드의 실행 흐름이 catch 블록으로 옮겨집니다.'); alert(`다음과 같은 에러가 발생했습니다: ${e.name}: ${e.message}`); }
try: 에러가 났을 때 원상복구를 시도할 코드. 에러 발생시 코드의 실행 흐름이 catch 블록으로 옮겨간다.
catch: 에러에 대한 정보를 담고 있는 객체(위 예제의 e)를 사용할 수 있다.
e.name: RangeError같은 에러의 이름
e.meassage: Invalid array length 에러 메시지
1 2 3 4 5 6 7 8 9 10 11 12 13
try { console.log('에러가 나기 직전까지의 코드는 잘 실행됩니다.'); /* new Array(-1); */ console.log(foo); console.log('에러가 난 이후의 코드는 실행되지 않습니다.'); } catch (e) { if(e.name === 'RangeError') { alert(`배열 생성자에 잘못된 인수가 입력되었습니다.`) } elseif (e.name === `ReferenceError`) { alert(`선언되지 않은 변수가 사용되고 있습니다.`) } console.log('코드의 실행 흐름이 catch 블록으로 옮겨집니다.'); }
finally: try블록 안에서의 에러 발생 여부와 관계 없이 무조건 실행되어야 하는 코드 return, break, continue등으로 코드의 실행 흐름이 즉시 이동되더라도 실행된다.
1 2 3 4 5 6 7 8 9
for (let i of [1, 2, 3]) { try { if (i === 3) { break; } } finally { console.log(`현재 i의 값: ${i}`); } }
finally블록은 catch 블록과도 같이 사용된다.
에러가 안 났을 때: try - finally
에러가 났을 때: try - 에러발생 - catch - finally
1-1. 직접 에러 발생시키기
코드를 다른 사람이나 미래의 내가 의도한 대로 사용하지 않을 경우 에러가 발생하도록 할 수 있다.
Error생성자, throw구문
1 2 3 4 5 6
const even = parseInt(prompt('짝수를 입력하세요')); if (even % 2 !== 0) { thrownewError('짝수가 아닙니다.'); } // 3을 입력할 경우 // Error: 짝수가 아닙니다.
1 2 3 4 5 6 7 8 9
throw'ggg'; // 던지는 것에 제한이 없으나 thrownewError('ddd') // 반드시 에러 객체를 던져야 한다.
const e = newError('짝수가 아니다') // Error는 어디서나 쓸 수 있는 객체(생성자)이다. e; // Error: 짝수가 아니다.
다음과 같이 내가 에러를 만들어 던질 수 있고, 이를 try...catch 구문으로 잡을 수 있다.
functionadd(x, y) { setTimeout(() => { // 이 콜백이 일단 태스크큐에 들어갔다가 호출 스택이 비워지면 실행된다. // 호출스택을 되감아 가면서 try를 찾아야하는데 // 이 콜백이 실행될 때는 이미 호출스택이 비워졌으므로 try를 찾을 수 없다. newArray(-1); }) return x + y; }
비동기 코드에서의 try...catch와 비동기 함수에서의 try...catch는 다르게 동작한다. (내부 동작 방식이 완전히 다르다. - 비동기 함수를 사용하면 예외처리도 보다 편하게 할 수 있다.)
비동기 함수 내부에서는, rejected 상태가 된 Promise객체를 동기식 예외처리 방식과 동일하게 try...catch...finally 구문으로 처리할 수 있다.
1 2 3 4 5 6 7 8 9 10 11
asyncfunctionfunc() { try { // ※ 단, Promise 객체에 대해 await 구문을 사용해야만 // 에러가 발생했을 때 catch 블록으로 코드의 실행 흐름이 이동한다. const res = await fetch('https://nonexistent-domain.nowhere'); } catch (e) { console.log(e.message); } }
오늘은 예외 처리(try...catch)와 모듈부분 진도를 나가고 리액트 실습(todoList)을 했다.
예외 처리 부분은 중간프로젝트 하면서 미리 읽어봤었는데, 호출스택과 태스크 큐에서의 코드 동작원리까지 같이 설명해주시니 좀 더 깊이있게 이해되는 것 같다.
리액트 컴포넌트 간의 변경사항 공유 때문에 조상 컴포넌트에서 상태를 관리하니까 컴포넌트를 나눌때마다 상태도 내려보내줘야해서 헷갈린다. 아직까지 배운 내용에서는 내가 중간 프로젝트때 가장 하위의 template에 여러 정보를 담은 객체를 상위 template으로부터 내려보내느라 그 사이의 하위 template에도 똑같이 그 객체를 내려보내줬던 방식과 크게 다르지 않은 것 같다. Redux나 Context를 통해서 이런 부분을 효율적으로 관리할 수 있다는데, 어떤식으로 만들었길래 그게 가능할까 신기하다.
강사님과 실습 시간에 어떤 메소드는 하위 컴포넌트에서 작성하고 어떤 메소드는 상태를 관리하고 있는 App 컴포넌트에 작성했다. 그러면서 역할과 책임에 대한 말씀을 해주셨다. 역할과 책임을 어떻게 정해야할지는 프로젝트를 진행할때마다 어렵게 느껴진다. 정답이 있는 건 아닌것 같은데 어떻게 코드를 짜야 역할과 책임에 맞게 코드를 작성하게 되는 건지 아직은 감이 잡히지 않아서 많이 만들어봐야겠다.
2. Algorithm 문제 풀이
다른 사람들 풀이를 보니까 숫자를 거꾸로 만드는 방법에 대해서 다른 방법을 알게 되었다. 어제 풀었지만 차마 포스팅할 수 없는 부끄러운 풀이라 좀 더 다듬으면 포스팅하자고 미뤄둔 문제도 숫자를 문자열화하는 식으로 풀었는데, 이번 문제를 좀 더 응용하면 새로운 방식으로 풀 수 있을 것 같다.
3. React 캘린더 만들기
moment.js를 사용하고 있는데, 캘린더의 body 부분의 표를 짜는 것만 구하면 수월할 것 같은데, 아직 못 만들었다. 하지만 어젯밤에 자기 전에 생각해본 것을 좀 더 수정해보면 될 것 같다.
리액트 연습삼아 리액트로 jQuery의 Datepicker같은 캘린더를 만들고 있다. 연습으로 만들어보는 건데 나에게 좀 어려운 것 같다. 리액트 문서도 다시 찾아보면서 하고 있다. 생각보다 한 것이 없는데 하루가 금방 지나가버려서 마음이 조급해진다. 좀더 시간을 잘 쪼개서 써야겠다.
금요일, 소프트웨어 공학 특강시간에 최우영 강사님이 보여주신 초보몽키의 개발공부로그라는 블로그에 감명받아 당장 블로그를 만들었다. 사실 그전에도 알고리즘 퀴즈 관련해서 구글에서 검색하면 거의 매번 보이던 블로그였는데, 학원 수강생이었다는 거에 충격이랄지 나도 저렇게 열심히 살아야겠다는 자극을 받았다.
hexo + icarus 테마 + github page로 블로그를 만들었다. 어느정도 파악해보려고 열어봤더니 EJS와 Stylus의 조합이라 당황스러웠다. 처음에는 체감상 Jekyll보다 어렵게 느껴졌는데(Jekyll 테마는 Sass였다.) 보다보니 익숙해지는 것 같다. 스타일은 아직 수정한 것이 없는데, 테마 자체의 디자인도 마음에 들지만 천천히 바꿔봐야겠다. 예를들면 모바일에서는 헤더를 상단에 픽스시킨다든지 정도의 커스터마이징은 필요해보인다.
만드는 것도 파악하는 것도 오래 걸려서, 저번 Jekyll 블로그처럼 학원 과정 끝나고 시간이 남으면 만들까 생각도 했는데, 일단 만들어놓고 천천히 바꾸자는 생각으로 만들었더니 하길 잘했다는 생각이 든다. github와 다르게 좀 더 글이 정제되어 보인다.
프로필 일러스트도 하는김에 만들었다.(이놈의 디자인 욕심…) 마침 github가 로키(턱시도 고양이)여서 로키 일러스트를 만들었다. 생각보다 금방 구상하고 오랜만에 일러 만지느라 헤맨 것 치고는 시간도 얼마 소요되지 않았다.(다행이다. 오래걸렸으면 자괴감이 들었을지도…)
지난 TIL과 알고리즘 풀이는 블로그로 옮길지, 아니면 기존 github 레포지토리로 남길지는 생각해봐야겠다.
2. 스캐폴딩, 퍼머링크 등 모르는 용어를 정리했다.
hexo로 블로그를 만들려다 보니 모르는 용어가 많았다. 스캐폴딩, CRUD, 퍼머링크를 정리하는 것으로 첫 포스팅을 하기로 했다. 대부분 데이터베이스나 서버 개발 쪽 아니면 아키텍처 관련 용어인듯하다. 데이터베이스 쪽도 생활코딩같은 강의를 통해서 틈틈이 공부해야겠다. 모르는 용어가 점점 줄어들었으면 좋겠다. (하지만 또 새로운 게 생겨나겠지…)
앞으로 할 일
React로 간단한 미니 프로젝트 만들기(학원내 스터디)
JSX 공부하기
모르는 게 많으니까 해야겠다 하는 것도 점점 많아진다. 그리고 알면 알수록 부족함이 보여서 더더욱 심해지는 것 같다. 이럴때일수록 무엇을 먼저 할지 우선 순위를 빠르게 정하고 밀고 나가야겠다.
블로그 만들면서 오랜만에 나를 프론트엔드 개발의 길로 이끈 블로그에 들어갔다. HyunSeob이라는 분의 블로그인데, hexo로 만들었다는 포스팅을 봤던 기억이 나서 다시 찾아봤다. (오랜만에 읽어보다 이 마음에 드는 테마도 건졌다.) 그 당시 gulp로 Sass를 써보다가 Webpack을 사용해보자면서 검색하다 보게 된 블로그였다. 포스팅이 엄청 많은 것은 아니지만, 1년 안에 포스팅한 내용이 Node.js, Webpack, AngularJS, CoffeScript, TypeScript등이어서 적잖게 충격을 받았다. 이렇게 열심히 트랜드를 쫓아가는구나, 나도 머물러 있으면 안 되겠다는 자극을 받았다.
건축용어로 비계라는 의미인데, 작업을 위해 임시로 설치한 가설 발판이나 가설 구조물을 말한다. 프로그래밍 쪽에서는 데이터베이스의 각 테이블에 대한 웹 페이지를 자동으로 생성하는 Dynamic Data 요소를 말한다.
이렇게 자동 생성된 웹 페이지를 통해 각 테이블에 대해 만들기, 읽기, 업데이트 및 삭제(CRUD) 작업을 수행할 수 있다. 스캐폴딩을 구성하는 템플릿을 사용해 효율적인 데이터 기반 웹 사이트를 신속하게 작성할 수 있다. 개발 중인 코드가 제 모습을 가지기 전까지 임시 스캐폴딩 코드를 코드 구조를 잡는데 사용한다고 한다.
정리하면 웹 개발 프레임워크(ASP.NET이나 Ruby on Rails등)에서 단순한 CRUD 서비스 프로세스를 구축하도록 지원하는 템플릿 개념인 것 같다.