Skip to main content

react에서 scrollTo 메소드로 스크롤 이동이 안될 때

· 2 min read

window.scrollTo(0, 0) 를 아무리 해봐도 스크롤이 맨 위로 올라가지 않을 때 많은 구글링을 한 뒤 다음과 같은 해결책을 적용해보았다

  • react-scroll 라이브러리를 써보고
  • 혹시 window 가 레이아웃 컴포넌트에 갇혀있어서 그런가 window 를 App.js 에서 Top 기능을 쓰는 곳까지 내려보고
  • jQuery 를 추가해서도 해보고
  • 별 짓을 다하다가

아무 것도 안 되서 리프레쉬하러 옥상에 갔다가 갑자기 든 생각이 있었다

잠깐만 component 라는게 하나의 element 안에서 쇼를 하는 거 잖아. 그럼 element 안에서 스크롤을 이동하면?

Footer.js
class Footer extends Component {
scrollToTop = (event) => {
document.getElementById("root").scrollTo(0, 0);
};

render() {
return (
<div>
<div className="top">
<a onClick={this.scrollToTop}>위로가기</a>
</div>
</div>
);
}
}

index.js 에서 ReactDOM 을 렌더링해주는 element(나같은 경우엔 div#root)를 찾아서 그 엘레먼트의 스크롤을 올려주자

여담

책에서 봤는데 뇌가 쉴 때 가장 좋은 아이디어가 나온다고 했는데, 사실인 것 같다

windows에 spring boot cli 설치하기

· 3 min read

스프링 부트를 설치해보자.

여기서 원하는 버전 스냅샷의 bin.zip 파일을 받아 압축을 풀어준다 예를 들면 spring-boot-cli-2.1.0.M1-bin.zipC:/spring에 압축을 풀면 된다.

정식버전은 여기의 zip 파일을 받으면 된다.

설정

환경변수를 3 개 추가해줘야한다

  • JAVA_HOME 변수에 JDK 설치 경로 (C:/Program Files/Java/jdk-10.0.2)
  • SPRING_HOME 변수에 방금 압축푼 spring cli 의 경로 (C:/spring)
  • path 변수에 %SPRING_HOME%\bin 경로

오류

git bash에서 org.springframework.boot.loader.JarLauncher 을(를) 찾거나 로드할 수 없습니다란 오류가 뜰 때는 spring/bin/spring파일을 열어 다음과 같이 수정하자

spring
# 16번째 줄 if 문 주석처리
# For Cygwin, ensure paths are in UNIX format before anything is touched.
# if $cygwin ; then
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
# fi

# 94번째 줄 if 문 주석처리
# if $cygwin; then
PRING_HOME=`cygpath --path --mixed "$SPRING_HOME"`
CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
# fi

출처

그래도 같은 오류가 발생한다면 spring 쉘 스크립트를 아예 새로 짜버리자

spring
[ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"`
# 아래에 다음 줄을 추가하자
# 이 구문은 스프링 홈 경로를 unix 스타일로 바꿔준다
[ -n "$SPRING_HOME" ] && SPRING_HOME=`cygpath --unix "$SPRING_HOME"`

# 94번째 줄을 다음과 같이 바꿔버리자
# if $cygwin; then
# SPRING_HOME=`cygpath --path --mixed "$SPRING_HOME"`
# CLASSPATH=`cygpath --path --mixed "$CLASSPATH"`
CLASSPATH=`cygpath --unix "$CLASSPATH"`
# fi

세 변수를 echo 찍었을 때 아래와 비슷하게 나오면 된다.

# echo $JAVA_HOME;
/d/Program Files/Java/jdk-10.0.2

# echo $SPRING_HOME;
/d/spring

# echo $CLASS_PATH;
.:/d/spring/bin:/d/spring/lib/spring-boot-cli-2.1.0.BUILD-SNAPSHOT.jar

그리고 마지막 줄의 명령어를 echo 할 때는 다음과 같다.

# echo "${JAVA_HOME}/bin/java" ${JAVA_OPTS} -cp "$CLASSPATH" org.springframework.boot.loader.JarLauncher "$@"
/d/Program Files/Java/jdk-10.0.2/bin/java -cp .:/d/spring/bin:/d/spring/lib/spring-boot-cli-2.1.0.BUILD-SNAPSHOT.jar org.springframework.boot.loader.JarLauncher

확인

$ spring --version

Spring CLI v2.1.0.M1

adonisjs에 response.sendStatus 추가하기

· One min read

express에 있는 sendStatus 기능을 활용하기 위해 start/hooks.js에도 다음 로직을 추가하자

start/hooks.js
const { hooks } = require("@adonisjs/ignitor");

hooks.after.providersBooted(() => {
const Response = use("Adonis/Src/Response");

Response.macro("sendStatus", function (status) {
this.status(status).send("");
});
});

추가 후엔 controller에서 response.sendStauts(403)과 같은 응답을 반환할 수 있다

redux-form 사용하기

· 9 min read

react 로 form 을 만들어 사용하는 일은 생각보다 많은 걸 해야한다.

  • onSubmit에 이벤트를 잡아서 event.target.fieldName.value로 하나씩 체크를 해서 validating 을 하다가
  • 귀찮아질 때 쯤 function 을 만들어서 관리를 하고
  • 자주 사용하는 form input 을 컴포넌트로 만들며
  • form 에 초기값을 넣어줘야할 경우 (edit 페이지 같은) 값을 가져와 하나하나 바인딩하고
  • input 에 값이 바뀌었는지에 따라 submit 버튼을 활성, 비활성화 하는 로직도 있어야한다

번거로운 일을 하는 것 보다 redux-form 라이브러리를 사용해보자 검색시에 나오는 포스트들은 아주 기본적인 예제 밖에 없어서 문서를 매번 헤집는 시간이 필요했다.

먼저 이 라이브러리를 쓰기 위해선 다음 사전 지식이 필요하다

$ yarn add redux-form

redux-form 의 리듀서를 연결시켜줘야한다

rootReducer
import { reducer as formReducer } from "redux-form";

const rootReducer = combineReducers({
// form 키를 사용해야한다
form: formReducer,
});

export default rootRecuder;

사용

redux-form component 와 redux-form 이 들어갈 compoent 를 만들어야한다 편의상 Login componentLoginForm component라고 하자

Login
import React, { Component } from "react";
import { connect } from "react-redux";
import LoginForm from "LoginForm";

class Login extends Component {
submitLoginForm = (formData) => {
console.log("LoginFormData => ", formData);
// this.props.loginAuth(formData)
};

render() {
return <LoginForm onSubmit={this.submitLoginForm} />;
}
}

export default connect()(Login);

LoginComponent에서는 딱히 특별한게 없이 LoginForm Component 를 호출하고 함수 하나를 내려준 게 끝이다

LoginForm
import React, { Component } from "react";
import { Field, reduxForm } from "redux-form";

class LoginForm extends Component {
render() {
const {
handleSubmit, // submit 이벤트를 redux-form에서 관리하게 수정
pristine, // form이 변경점이 없을 경우 true
submitting, // form이 submit 중일 경우 true
} = this.props;

return (
<form onSubmit={handleSubmit}>
<Field
name="id"
type="text"
component="input"
placeholder="아이디를 입력하세요"
/>
<Field
name="password"
type="password"
component="input"
placeholder="비밀번호를 입력하세요"
/>
</form>
);
}
}

export default reduxForm({
form: "LoginForm", // formReducer에 어떤 이름으로 이 폼을 등록할지
})(LoginForm);

redux-form에서 Field ComponentreduxForm High Order Component를 가져온 뒤 input tag 대신 Field Component 의 component 속성으로 input 을 만들어준다 그리고 해당 폼을 reduxForm 으로 감싸 redux 와 연결한다

handleSubmit은 onSubmit prop 으로 들어온 함수를 폼과 연결해준다 (event.preventDefault() 및 폼의 필드들을 serialize 해서 object 로 바꿔 onSubmit 함수의 parameter 로 넘겨준다)

말이 좀 어렵다면 Login.jssubmitLoginFormconsole.log(formData)의 구조를 보자

{
"id": "idValue",
"password": "passwordValue"
}

오 꽤 나이스하다 하지만 필드만 덩그라니 있는 경우는 없다 더 업그레이드 해보자

스타일이 필요한 필드

보통의 필드들은 label이 들어가 있고, selectBox, checkBox의 경우엔 스타일을 주기위해 대부분 외부 라이브러리를 쓴다 이럴 때 Field Component 의 component 속성에 원하는 모양을 만들어서 넣어주면 된다

LoginForm
...

const renderInputField = ({ input, type, label, placeholder, meta: { touched, error }}) => {
return (
<div>
<label>{label}</label>
<input {...input} type={type} placeholder={placeholder} />
{ touched && error &&
<span className="error">{error}</span>
}
</div>
)
}

class LoginForm extends Component {

render() {
const {
handleSubmit,
pristine,
submitting
} = this.props

return (
<form onSubmit={handleSubmit}>
<Field
name="id"
type="text"
label="아이디"
component={renderInputField}
placeholder="아이디를 입력하세요"
/>
<Field
name="password"
type="password"
label="비밀번호"
component={renderInputField}
placeholder="비밀번호를 입력하세요"
/>
</form>
)
}
}

renderInputField라는 함수형 컴포넌트를 만들어서 Field.component prop 에 넣긴했는데, div>input을 렌더링 해주는 것만 보이고 엄청난 파라미터들 때문에 정신이 아득할 것이다

component prop 에 함수형 컴포넌트를 넣으면 field prop 을 받게 된다 여기엔 내려준 prop 뿐 아니라 reduxForm 에서 넣어준 prop 도 존재한다 원래 해당 렌더링 컴포넌트는 아래와 같은 모습이다

const renderInputField = (field) => {
return <div>...</div>;
};

그럼 먼저 reduxForm 에서 넣어준 prop 을 보자

  • field.input: input 은 모든 HTML input attribute 를 가지고 있는게 아니라 다음 몇가지만 갖고 있다
    • checked
    • name
    • onBlur
    • onChange
    • onDragStart
    • onDrop
    • onFocus
    • value
  • field.meta: meta 는 해당 input 의 상태에 관한 데이터가 있다
    • touched: 해당 input 이 한 번이라도 클릭이 되어졌는지
    • valid: 해당 input 값이 valid 한지
    • error: 해당 input 값에 에러가 있으면 error message 가 들어온다
    • ... 나머진 문서를 참조하자

이제 type, label, placeholder 는 field.input prop 에 없어서 직접 넣어줬고, meta 는 이미 정의되어 있었구나 란 renderInputField 함수의 props 가 보일 것이고 Field Component 의 구조를 마음대로 변경할 수 있게 되었다

동적으로 value 를 바꿔야할 때

checkbox, select 의 경우엔 스타일이 들어가면 element 의 onClick 를 잡아서 hidden field 의 데이터를 바꿔줘야한다 checkbox 전체 영역을 클릭할 때마다 기존의 값을 toggle 해주는 component 를 만들 수 있다

LoginForm
const renderCheckboxField = ({ input, label }) => {
return (
<div onClick={(event) => input.onChange(!input.value)}>
<i
className={`fa ${!input.value ? "fa-square-o" : "fa-check-square-o"}`}
/>
<label>{label}</label>
<input type="hidden" {...input} />
</div>
);
};

value 에 따라서 font-awesome icon이 변경된다

함수로 값을 변경해야할 경우가 있을 수도 있다 이럴 땐 reduxForm prop 의 change 메소드를 사용하면 된다 바꿀 input 의 name 속성과 value 값을 넘겨주자

changeInputValue = (targetInputName, val) => {
this.props.change(targetInputName, val);
};

초기값

로그인 폼에는 아이디 저장 기능을 붙히면 폼이 초기화될 때 그 값을 가져와야한다

Login
...

class Login extends Component {
state = {
userId: ''
}

componentDidMount() {
const userId = localStorage.getItem('userId')

this.setState({
userId
})
}

render() {
return (
<LoginForm
onSubmit={this.submitLoginForm}
initialValues={{
id: this.state.userId
}}
/>
)
}
}

Login Component 에는 초기값을 가져와서 initialValues prop 안에 넣어준다

LoginForm
class LoginForm extends Component {
render() {
return <form>...</form>;
}
}

export default reduxForm({
form: "LoginForm",
// 이 값은 LoginForm 컴포넌트가 로드되고 나서
// initialValues가 바뀔 경우 폼 값도 업데이트가 되야되는지의 여부이다
enableReinitialize: true,
})(LoginForm);

localStorage.userId에 값이 들어있다면 그 값으로 id Field 가 초기화 된다 컴포넌트가 로딩되기 전에 값이 정해져있다면 enableReinitialize 옵션이 없어도 되지만, 수정 폼처럼 기존 데이터 fetch 후 또는 componentDidMount 후에 값을 가져온다면 해당 옵션값을 true로 넣어줘야한다

validation

여기까지 이해했다면 validation 은 큰 어려움 없이 문서의 예제소스를 참조하면 붙힐 수 있다

여담

react 라이브러리들은 읽은 뒤 바로 적용하긴 힘들다고 생각한다 매번 삽질이 필요한듯

react-intl로 번역 적용하기 (react i18n)

· 3 min read

리액트 앱에서 번역기능을 사용하려면 react-intl 패키지를 사용하면 된다. 근데 Documentation을 보면 format 기능들, HOC로 데이터를 넣는 등 여러 기능 덕에 정신이 아득하다

step by step으로 간단하게 적용해보자

npm install react-intl

번역 데이터 생성

편의상 root에 locale.js로 만들었다. 서비스시엔 locale 폴더에 언어별로 파일을 나눠 관리하자.

locale.js
export default {
en: {
hello: "Hello",
},
ko: {
hello: "안녕하세요",
},
ja: {
hello: "こんにちは",
},
};

연동

index.js
import { IntlProvider, addLocaleData } from "react-intl";
// 이 서브 라이브러리들이 내 locale 파일을 사용할 수 있게 해준다
import en from "react-intl/locale-data/en";
import ko from "react-intl/locale-data/ko";
import ja from "react-intl/locale-data/ja";
import locale from "./locale";

addLocaleData([...en, ...ko, ...ja]);

// 저장되어 있는 언어 데이터를 가져온다
const defaultLang = localStorage.getItem("lang") || "en";

ReactDOM.render(
<Provider store={store}>
<IntlProvider locale={defaultLang} messages={locale[defaultLang]}>
<App />
</IntlProvider>
</Provider>,
document.getElementById("root"),
);

사용

import { FormattedMessage } from "react-intl";

<FormattedMessage id="hello" />;
// 저장되어 있는 언어 값에 따라 Hello, 안녕하세요, こんにちは 중 하나가 보여진다

inject

placeholder 등에서 텍스트만 필요할 때 component를 사용하지않고 다음과 같이 intl을 주입해서 사용한다.

import React, { Component } from "react";
import { injectIntl } from "react-intl";

class SignupForm extends Component {
render() {
const { intl } = this.props;

return (
<form>
<input
type="text"
name="id"
placeholder={intl.formatMessage({
id: "hello",
})}
/>
</form>
);
}
}

export default injectIntl(SignupForm);

child

HOC를 사용하지 않을 경우 FormattedMessage의 child로 번역된 문구를 받으면 된다

<FormattedMessage id="hello">
{(hello) => <input type="text" name="id" placeholder={hello} />}
</FormattedMessage>

dynamic

Hello, {Gracefullight} 처럼 동적으로 문구가 변해야할 경우valuesprop을 활용하자 먼저,locale.js에서 변수가 될 부분을 로 감싸준다

locale.js
export default {
en: {
helloUser: "Hello {user}",
},
ko: {
helloUser: "{user} 안녕하세요",
},
ja: {
helloUser: "{user} こんにちは",
},
};

values 아래 object로 변수 값을 넣어주면 된다

<FormattedMessage
id="helloUser"
values={{
user: "Gracefullight",
}}
/>

여담

mo, po 파일 건들던 시간들이 너무 아깝다

나를 위한 면접 (Frontend Developer Interview Questions)

· 14 min read

FE Interview Questions를 아는 만큼 답해보았다 지극히 주관적이라 정답이 아닐 수 있습니다

사용자의 액션에 의해 이벤트 발생 시 이벤트는 document 레벨까지 버블링 되어 올라간다. 이 때문에 자식 엘리먼트에서 발생하는 이벤트를 부모 엘리먼트에서도 감지할 수 있다. 이러한 동작을 이용해 사용할 수 있는 방법이 event delegation이다. 특정 엘리먼트에 하나하나 이벤트를 등록하지 않고 하나의 부모에 등록하여 부모에게 이벤트를 위임하는 방법

// 버튼마다 onClick Event Listener 등록
document.getElementById("btn1").addEventListener("click", (event) => {});

document.getElementById("btn2").addEventListener("click", (event) => {});

document.getElementById("btn3").addEventListener("click", (event) => {});

// Event Delegation
document.getElementById("div").addEventListener("click", (event) => {
switch (event.target.id) {
case "btn1":
break;
case "btn2":
break;
case "btn3":
break;
}
});

this는 어떻게 작동하는가?

  • Global Context: window

  • Function Context: "use strict" ? 실행 시 할당 : window

  • Object method: 자기 자신 객체

  • Object Prototype Chain: 자기 자신 객체

  • Getter, Setter: get 또는 set 되는 속성 객체

  • 생성자: 생성된 자신

  • call, apply: 지정해 주는 객체

  • bind: 바인딩 될 때의 this

  • Dom Event: event.currentTarget === event.target === this

  • in-line Event: Element

  • 출처

prototype 기반 상속은 어떻게 하는가?

Object.create로 prototype을 확장하던지 proto로 prototype link를 걸던지

AMD와 CommonJS는 뭐고 어떻게 생각하나?

  • AMD: requireJS
  • CommonJS: NodeJS 방식

결국엔 UMD 패턴으로 모듈을 만들어야한다. 둘 다 실무에서 쓰려면 번들링이 필요하고, Webpack 쓸 바에 두 개 다 필요없이 import 구문으로 처리하겠다.

다음 코드가 즉시 호출 함수 표현식(IIFE)로 동작하지 않는 이유?

Immediately Invoked Function Expression

// function foo(){}()

함수선언문을 어떻게 실행하냐 함수를 괄호로 감싸면 됨

null과 undefined 그리고 undeclared의 차이점

  • null: 선언되었지만 값 없음
  • undefined: 선언조차 안됨
  • undeclared: 선언문 (var 등) 없이 전역변수로 할당되는 유효범위를 지정하지 않은 건데 린팅에 어긋남

클로져(Closure)는 무엇이며, 어떻게/왜 사용?

  • 무엇: 초기화시의 상태를 내부에 가지고 있는 형태의 함수
  • 어떻게:return으로 공개할 메소드 또는 데이터를 꺼냄
  • 왜: private 기능 구현, for문에서 context 참조

익명함수(anonymous functions)는 주로 어떤 상황에서 사용?

  • 함수 표현식 (변수로 함수 선언), callback

당신의 코드를 어떻게 구성하는지?

  • ESLint airbnb + prettier + commitlint

호스트 객체 vs 네이티브 객체

  • Host Objects: 사용자에 의해 생성된 객체
  • Native Objects: 브라우저 또는 구동 엔진에 내장된 객체

다음 코드의 차이점?

function Person() {} // 선언문
var person = Person(); // 함수 표현식
var person2 = new Person(); // 생성자

.call과 .apply의 차이점?

  • call()은 함수에 전달될 여러 개의 인자를 받는데 비해
  • apply()는 배열로 된 하나의 인자를 받는다

Function.prototype.bind?

  • 호출될 때의 함수의 this 값을 넘겨준 값으로 바인딩

document.write()는 언제 사용?

  • 스크립트 심을 때 document.write는 block 되지 않으므로
  • 근데 appendChild로 심을 수 있어서 안씀

AJAX

  • XMLHttpRequest 객체를 사용해 비동기 방식으로 서버와 통신을 하는 것
  • 장점: 화면 전환 없이 특정 영역만 갱신 (업데이트) 가능
  • 단점: 없음

JSONP vs AJAX

  • JSONP는 스크립트 태그를 심어서 로드될 때 콜백 함수를 호출하는 거고 get만 가능
  • Same origin policy를 벗어날 수 있음

호이스팅

  • 자바스크립트 엔진이 실행 컨텍스트를 생성하면서 scope 를 정의 할때
  • 코딩 순서에 상관없이 선언부에 대한 처리를 맨 위로 끌어올려 먼저 해석하는 것

Event Bubbling

  • 자식 이벤트가 부모로 전달 되는 것
  • event.stopPropagation으로 막을 수 있음
  • 다른 리스너를 실행시키지 않는건 event.stopImmediatePropagation

속성(Attribute) vs 요소(property)

  • Attribute: HTML 요소에 추가적인 정보 전달
  • Property: Attribute에 대한 HTML DOM tree에서의 표현

내장된 JavaScript 객체를 확장하는 것이 좋지 않은 이유?

  • 그것을 참조한 모든 객체가 확장한 구문을 따라가기 때문에

document load event vs DOMContentLoaded event

  • document.load: DOM 안의 모든 게 로딩이 끝나야 발생 (더 느림)
  • DOMContentLoaded: DOM 초기화시 발생

== vs ===

  • 타입 체크

동일출처정책(the same-origin policy)

  • js나 문서가 다른 origin에서 fetch되지 못하게 하는 정책

삼항식(Ternary statement)을 사용하는 이유?

  • if else의 간소화

use strict

When adding 'use strict';, the following cases will throw a SyntaxError before the script is executing:

  • Octal syntax var n = 023;

  • with statement

  • Using delete on a variable name delete myVariable

  • Using eval or arguments as variable or function argument name

  • Using one of the newly reserved keywords (in prevision for ECMAScript 2015): implements, interface, let, package, private, protected, public, static, and yield

  • Declaring function in blocks if (a < b)

  • Obvious errors

    • Declaring twice the same name for a property name in an object literal {a: 1, b: 3, a: 7} This is no longer the case in ECMAScript 2015 (bug 1041128).
    • Declaring two function parameters with the same name function f(a, b, b)
  • 출처

전역 scope를 사용했을 때

  • 장점: 어디에서나 접근 가능
  • 단점: release 꼬임, 추적 힘듦.

때때로 load event를 사용하는 이유? 단점과 대안은?

  • DOM의 모든 게 로드가 끝난 후에 이벤트를 걸어야할 것이 있을 때 사용 (lazy loading)

SPA에서 SEO에 유리하도록 만들기 위한 방법

  • Sitemap, JSON-LD, UA가 Bot일 경우 SSR

Promise vs Callback

  • 장점: Chaining, 가독성, 여러 Promise를 한 번에 제어 가능
  • 단점: 없다

JavaScript를 디버깅할 때 사용하는 도구?

  • VSCode, Chrome Dev Tools

객체 안의 속성과 배열의 아이템을 순회할 때 사용하는 문법?

  • Object: for in hasOwnProperty, Object.keys().forEach, for of
  • Array: for, Array.forEach, for of

mutable object vs immutable object

  • mutable: 변경 가능
  • immutable: 변경 불가능, Object freeze 또는 Object assign으로 metadata 수정

JavaScript에서 immutable 객체의 예

  • String: 문자열이 생성되면 그 다음에 수정할 수 없음
  • Symbol: 유일한 값

immutability의 장단점?

  • 장점: 변경점 찾기 및 디버깅이 쉬움
  • 단점: mutable한 코드보다 훨씬 느림

자신의 코드에서 불변성(immutability)을 어떻게 달성할 수 있나요?

  • eslint-plugin-functional 또는 immutableJS

동기방식과 비동기 방식 함수의 차이

  • 은행 번호표

event loop

  • JS에서 사용하는 동시성 처리 모델
// 현재 아무 메시지도 없다면 새로운 메시지 도착을 동기적으로 기다림
while (queue.waitForMessage()) {
queue.processNextMessage();
}

call stack

  • 함수 호출시 저장되는 스택, 재귀를 생각하면 됨

task queue

  • 비동기 함수가 저장되는 큐, call stack이 비워지면 실행 됨

function foo() 와 var foo = function() 에서 foo 의 차이

  • 전자는 호이스팅, 후자는 안 됨

let, var, const의 차이점

  • var: function-scoped 로 호이스팅, 재선언 가능
  • let: block-scoped 로 재선언 불가, 재할당 가능
  • const: block-scoped 로 재선언 불가, 재할당 불가

test code를 작성하면서 개발하는 방식의 장단점

  • 장점: 결과가 나와있고 로직을 맞추면되서 디버깅, 리팩토링이 쉬워지고 유지보수에도 좋음
  • 단점: 시간

test code를 테스트하는 툴 사용경험

  • Jest, Puppeteer, supertest

단위 테스트 vs 기능 테스트

  • unit test: unit 즉 method 별로 테스트
  • functional test: 전체 시스템에서 기능을 테스트, 많은 method와 서비스가 연결되어있음

code style linting tool을 사용했을때 장점?

  • 모두가 일관된 스타일로 코딩 가능, 가독성 증가

성능관련 이슈들을 발견하기 위해서 사용하는 방법?

  • 실행시간 timestamp 로깅
  • Chrome Dev Tools Performance 탭

웹사이트 scrolling 성능을 향상시키기 위한 방법?

  • throttle

브라우저의 layout, painting, compositing

  • Layout: 각 element가 차지하는 공간과 배치할 공간을 결정

  • Painting: element를 그리고, element의 모든 시각적 부분을 그리는 작업

  • Compositing: element를 올바른 순서로 화면에 그려 페이지를 렌더링

  • 출처

웹사이트의 assets을 여러 도메인으로 서빙했을 때 장점

브라우저 마다 다르지만 HTTP1.1에서는 하나의 호스트당 평균 4개의 동시 다운로드만 가능해서, 병렬 처리를 하기 위해 여러 도메인으로 서빙을 했었다 하지만 이젠 http2로 다 해결 가능

참고

URL로 접속했을 때 어떤 플로우로 화면에 웹사이트가 그려지는지 네트워크 관점

  • 로컬 DNS에서 URL IP 확인
  • 3way handshake로 IP에 TCP 연결 설정 후 HTTP 헤더와 Body 요청 전송
  • 응답받은 HTTP 헤더와 Body로 웹사이트 렌더링

Long-Polling과 Websocket, Server-Sent Event

  • Long-Polling: response를 닫지 않고 계속 보내주는 것
  • Websocket: subscribe 후에 양방향으로 메세지 교환
  • Server-Sent Event: Push, HTTP 사용

request headers

  • Expires headers tell the browser whether they should request a specific file from the server or whether they should grab it from the browser's cache.

  • Date: The date and time that the message was sent

  • The Age response-header field conveys the sender's estimate of the amount of time since the response (or its revalidation) was generated at the origin server.

  • The If-Modified-Since request-header field is used with a method to make it conditional: if the requested variant has not been modified since the time specified in this field, an entity will not be returned from the server; instead, a 304 (not modified) response will be returned without any message-body.

  • Do Not Track: disable either its tracking or cross-site user tracking

  • Cache-Control: Tells all caching mechanisms from server to client whether they may cache this object. It is measured in seconds

  • Transfer-Encoding: The form of encoding used to safely transfer the entity to the user. Currently defined methods are: chunked, compress, deflate, gzip, identity.

  • ETag: An identifier for a specific version of a resource.

  • X-Frame-Options can be used to indicate whether or not a browser should be allowed to render a page in a frame, iframe or object.

  • 출처

  • HTTP Cache 튜토리얼

HTTP Methods

  • GET: 리소스 요청
  • HEAD: 리소스 요청이나 body가 없음
  • POST: 리소스 생성
  • PUT: 리소스 업데이트
  • PATCH: 리소스 부분 업데이트
  • DELETE: 리소스 삭제
  • OPTIONS: Cross Domain request에서 해당 메소드가 안전한지 확인하기 위해 사용

나를 위한 면접 (ALL)

· 21 min read

실무에 치이느라 기본적인 걸 까먹을까봐 정리해보자 정보처리 에서 컴퓨터 기초를 보면 되고. 지극히 주관적이라 정답이 아닐 수 있습니다

미적분

편미분까진 안물어보더라

베이지안 확률

사후확률 = 가능도 * 사전확률 / 증거

// 붉은 점이 얼굴에 보일 때 수두 환자일 확률은 어떻게 구할까?
P(수두 | 붉은점) = P(붉은 점 | 수두) * P(수두) / P(붉은점)

자료구조

B Tree

m-원 트리의 단점임 한쪽으로 편중된 트리를 생성되는 경우를 보완하고자 루트노드로부터 리프노드의 레벨을 같도록 유지한 트리

BASIS FOR COMPARISONB-TREEBINARY TREE
Essential constraintA node can have at max M number of child nodes(where M is the order of the tree).A node can have at max 2 number of subtrees.
UsedIt is used when data is stored on disk.It is used when records and data are stored in RAM.
Height of the treelogM N (where m is the order of the M-way tree)log2 N
ApplicationCode indexing data structure in many DBMS.Code optimization, Huffman coding, etc.

space complexity B-tree is O(n). Insertion and deletion time complexity is O(logn).

출처

B+ Tree

B+은 index node와 data node로 구성

출처

B* Tree

B-tree의 경우에 각 노드가 최소한 반 이상 차 있어야 하는 조건각 노드가 최소한 2/3이상 차있어야 한다로 변경하면 이것이 B*tree이다

공간 활용도를 높일 수 있다. 출처

Binary Tree

모든 노드의 차수가 2 이상을 넘지 않는 트리를 말한다, 왼쪽자식노드는 부모노드의 값보다 작이야하며 오른쪽 자식노드는 부모노드의 값보다 커야한다.

Binary Search Tree

이진탐색(binary search)과 연결리스트(linked list)를 결합한 자료구조의 일종입니다. 이진탐색의 효율적인 탐색 능력을 유지하면서도, 빈번한 자료 입력과 삭제를 가능하게끔 고안됐습니다.

  • 모든 원소는 서로 다른 유일한 키를 가짐
  • 왼쪽 서브트리에 있는 원소들의 값은 그 루트의 값보다 작음
  • 오른쪽 서브트리에 있는 원소의 값들은 그 루트의 값보다 큼
  • 왼쪽 서브트리와 오른쪽 서브트리도 이진 탐색 트리임

O(n), 완전 이진 트리에 가까울 수록 O(log2(n))

출처

Insertion Sort

앞에서부터 하나씩 비교하여 위치를 삽입

O(n^2)

function insertionSort(unsortedList) {
const len = unsortedList.length;
for (let i = 1; i < len; i++) {
// Copy of the current element.
const tmp = unsortedList[i];
// Check through the sorted part and compare with the number in tmp. If large, shift the number
for (var j = i - 1; j >= 0 && unsortedList[j] > tmp; j--) {
// Shift the number
unsortedList[j + 1] = unsortedList[j];
}
// Insert the copied number at the correct position
// in sorted part.
unsortedList[j + 1] = tmp;
}
}

출처

Selection Sort

하나씩 뒤로 비교하여 최소값을 맨 앞으로 삽입

O(n^2)

function selectionSort(items) {
const length = items.length;
for (let i = 0; i < length - 1; i++) {
// Number of passes
// min holds the current minimum number position for each pass; i holds the Initial min number
let min = i;
// Note that j = i + 1 as we only need to go through unsorted array
for (let j = i + 1; j < length; j++) {
// Compare the numbers
if (items[j] < items[min]) {
// Change the current min number position if a smaller num is found
min = j;
}
}
if (min != i) {
// After each pass, if the current min num != initial min num, exchange the position.
// Swap the numbers
const tmp = items[i];
items[i] = items[min];
items[min] = tmp;
}
}
}

출처

Quick Sort

pivot을 하나 뽑는다 보통 list.length / 2 -1 을 선택한다. pivot 앞에는 pivot보다 작게, pivot 뒤에는 pivot보다 크게 값을 바꿔치고 재귀를 돌린다

최악 O(n^2), 평균 O(nlogn)

const quickSort = (list) => {
if (list.length < 2) {
return list;
}

const pivot = list[0];
// const pivot = list[Math.floor(list.length / 2)];
const smaller = list.filter((item) => item < pivot);
const bigger = list.filter((item) => item > pivot);

return [...quickSort(smaller), pivot, ...quickSort(bigger)];
};

참고

List

Linked List

List vs Linked List

리스트가 대부분 좋다 연결 리스트는 중간에 삽입 삭제시 좋다 리스트는 끝에 넣을 떄 좋다

연결 리스트는 검색시 링크를 따라가므로 비효율적이다. 리스트가 순차라 검색시에 좋다. 공간의 효율성은 연결 리스트

효율성 비교

function LinkedList() {
this.head = null;
}

LinkedList.prototype.push = function (val) {
const node = {
value: val,
next: null,
};

if (!this.head) {
this.head = node;
} else {
current = this.head;
while (current.next) {
current = current.next;
}
current.next = node;
}
};

const sll = new LinkedList();
sll.push(2);
sll.push(3);
sll.push(4);

// check values by traversing LinkedList
sll.head; // Object {data: 2, next: Object}
sll.head.next; // Object {data: 3, next: Object}
sll.head.next.next; // Object {data: 4, next: null}

출처

Graph

  • 그래프(Graph)는 연결되어 있는 원소간의 관계를 표현하는 자료구조이다. 나와 연관된 인간 관계를 나타내는 인맥 지도, 수도 배관에 대한 배수 시스템, 물질의 분자 구조 등은 연결 구조가 다양하기 때문에 선형 자료 구조나 트리로는 표현 할 수가 없다.
  • 그래프는 연결할 객체를 나타내는 정점(vertex)와 객체를 연결하는 간선(edge)의 집합으로 구성된다. 그래프 G를 *G=(V,E)*로 정의하는데, V는 그래프에 있는 정점들의 집합을 의미하고, E는 정점을 연결하는 간선들의 집합을 의미한다.

출처

// Implement a Graph
// basic operations:
// - add vertex (node)
// - add edge (node -> node)

function GraphNode(val) {
this.val = val;
this.edges = {};
}

function Graph() {
this.vertices = {};
}

// O(1) operation
Graph.prototype.addVertex = function (val) {
// add vertex only if it does not exist.
if (!this.vertices[val]) {
this.vertices[val] = new GraphNode(val);
}
};

// O(E) operation - edges
Graph.prototype.removeVertex = function (val) {
if (this.vertices[val]) {
// once you remove a vertex, you need to remove all edges pointing
// to the vertex.
delete this.vertices[val];

Object.keys(this.vertices).forEach(
function (key, index) {
if (this.vertices[key].edges[val]) {
delete this.vertices[key].edges[val];
}
}.bind(this),
);
}
};

// O(1) operation
Graph.prototype.getVertex = function (val) {
return this.vertices[val];
};

// O(1) operation
Graph.prototype.addEdge = function (start, end) {
// check to see if vertices exists.
// if it exists, set the edges and be done.
if (this.vertices[start] && this.vertices[end]) {
// check to see if edge exists, if it does, increment it's weight
if (this.vertices[start].edges[end]) {
this.vertices[start].edges[end].weight += 1;
} else {
// edge does not exist, set weight to 1.
this.vertices[start].edges[end] = { weight: 1 };
}
}
};

// O(1) operation
Graph.prototype.removeEdge = function (start, end) {
if (this.vertices[start] && this.vertices[end]) {
if (this.vertices[start].edges[end]) {
delete this.vertices[start].edges[end];
}
}
};

// O(1) operation
Graph.prototype.getEdge = function (start, end) {
return this.vertices[start].edges[end] || null;
};

Graph.prototype.neighbors = function (val) {
return this.vertices[val] ? this.vertices[val].edges : null;
};

const graph = new Graph();

graph.addVertex(5);
graph.addVertex(2);
graph.addVertex(6);
graph.addVertex(7);
graph.addEdge(2, 5);
graph.addEdge(6, 7);
graph.addEdge(7, 5);
console.log(graph.getEdge(2, 5));
console.log(graph.getEdge(6, 7));
graph.removeVertex(5);
console.log(graph.getEdge(2, 5));
console.log(graph.neighbors(6));
console.log(graph.neighbors(5));

출처

알고리즘

시간복잡도

  • 알고리즘이 문제를 해결하는 데에 얼마나 걸리는 지를 분석하는 것
  • 프로그램을 실행시켜 완료하는데 걸리는 시간을 의미

빅오표기법

  • 실행 시간은 최대한 이만큼 커지지만 더 천천히 커질 수도 있다는 의미인 점근적 표기법 형태를 사용하는 것 (점근 표기법)
  • 최악의 경우를 표기하는 방법

빅오에서 n이 의미하는것

  • 입력 데이터 개수

최단거리 알고리즘

  • 다익스트라 알고리즘
  • A* 알고리즘

슬라이딩 윈도우

이 문제가 도움 될 듯

데이터베이스

정규화

  • 관계형 데이터베이스의 설계에서 중복을 최소화하게 데이터를 구조화하는 프로세스

목적

  • 이상이 있는 관계를 재구성하여 작고 잘 조직된 관계를 생성하는 것에 있다.
  • 일반적으로 정규화란 크고, 제대로 조직되지 않은 테이블들과 관계들을 작고 잘 조직된 테이블과 관계들로 나누는 것을 포함한다.

비정규화

  • 과도한 정규화로 인해서 테이블의 수가 증가하게 되면, 다수의 JOIN이 발생함에 따라서 성능 저하가 발생할 수 있다. 보통의 경우 정규화 과정을 모두 거친 다음 마지막 단계에서 비정규화를 실시한다.
  • 단, 테이블을 합치는 것만이 비정규화는 아니다.

인덱싱 자료구조

B-Tree 알고리즘은 가장 일반적으로 사용되는 인덱스 알고리즘으로서, 상당히 오래전에 도입된 알고리즘이며 그만큼 성숙해진 상태입니다. B-Tree 인덱스는 칼럼의 값을 변형하지 않고, 원래의 값을 이용해 인덱싱하는 알고리즘 입니다.

R-Tree 다차원 공간

Hash 인덱스 알고리즘은 컬럼의 값으로 해시 값을 계산해서 인덱싱하는 알고리즘으로, 매우 빠른 검색을 지원합니다. 하지만 값을 변형해서 인덱싱하므로, 전방(Prefix) 일치와 같이 값의 일부만 검색하고자 할 때는 해시 인덱스를 사용할 수 없습니다. Hash 인덱스는 주로 메모리 기반의 데이터베이스에서 많이 사용합니다.

Fractal-Tree 알고리즘은 B-Tree의 단점을 보완하기 위해 고안된 알고리즘입니다. 값을 변형하지 않고 인덱싱하며 범용적인 목적으로 사용할 수 있다는 측면에서 B-Tree와 거의 비슷하지만 데이터가 저장되거나 삭제될 때 처리 비용을 상당히 줄일 수 있게 설계된 것이 특징입니다. 아직 B-Tree 알고리즘만큼 안정적이고 성숙되진 않았지만 아마도 조만간 B-Tree 인덱스의 상당 부분을 대체할 수 있지 않을까 생각합니다.

출처

캐싱시 자료구조

  • Hash Table and a Linked List

디비 클러스터링

개념 참조 운영 참조

Isolation level

  • Read Uncommitted
  • Read Committed
  • Repeatable Read
  • Serializable

개념 참조 이하 참조

Read Uncommitted Isolation Level

SELECT 문장을 수행하는 경우 해당 데이터에 Shared Lock이 걸리지 않는 Level입니다. 따라서, 어떤 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 B라는 아직 완료되지 않은(Uncommitted 혹은 Dirty) 데이터 B를 읽을 수 있습니다.

Read Committed Isolation Level

SQL Server가 Default로 사용하는 Isolation Level입니다. 이 Level에선 SELECT 문장이 수행되는 동안 해당 데이터에 Shared Lock이 걸립니다. 그러므로, 어떠한 사용자가 A라는 데이터를 B라는 데이터로 변경하는 동안 다른 사용자는 해당 데이터에 접근할 수 없습니다.

Repeatable Read Isolation Level

트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 Shared Lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정이 불가능합니다. 가령, Select col1 from A where col1 between 1 and 10을 수행하였고 이 범위에 해당하는 데이터가 2건이 있는 경우(col1=1과 5) 다른 사용자가 col1이 1이나 5인 Row에 대한 UPDATE이 불가능합니다. 하지만, col1이 1과 5를 제외한 나머지 이 범위에 해당하는 Row를 INSERT하는 것이 가능합니다.

Serializable Isolation Level

트랜잭션이 완료될 때까지 SELECT 문장이 사용하는 모든 데이터에 Shared Lock이 걸리므로 다른 사용자는 그 영역에 해당되는 데이터에 대한 수정 및 입력이 불가능합니다. 예를 들어, Repeatable Read의 경우 1에서 10 사이에 해당되는 데이터에 대한 UPADTE이 가능하였습니다. 하지만 이 Level에서는 UPDATE 작업도 허용하지 않습니다.

ACID

원자성, 일관성, 독립성, 영속성

Atomicity

트랜잭션 내의 명령은 반드시 완벽히 수행 모두가 수행되거나 오류시 전부가 취소되어야함

Consistency

DB의 전체 요소는 트랜잭션 수행 전과 트랜잭션 수행 완료 후의 상태가 같아야함 (질량 보존 법칙처럼)

Isolation

둘 이상의 트랜잭션이 병행 실행되는 경우 다른 트랜잭션 연산이 끼어들 수 없음 수행 중인 트랜잭션은 완료될 때 까지 다른 트랜잭션에서 참조 불가.

Durability

시스템이 고장나도 영구적으로 반영

CRUD

Create(생성), Read(읽기), Update(갱신), Delete(삭제)

InnoDB와 MyISAM의 차이

image from hexo

MariaDB가 MySQL보다 나은점

증분 백업

정해진 시간을 기준으로 그 이후에 변경된 파일만을 백업하는 방식

서버

서버 로드밸런싱

설명할 수 있음

이미지 클러스터링

k-means (피벗 추출 후 2진수 비교인데 아직 이해하기가 힘들다, 18년 말에는 딥러닝을 파보자!)

Build Process 설명

Deploy Server

Git Flow

브랜치 관리 전략

  • feature: develop에서 분기 후 기능 개발 후 develop으로
  • develop: 개발 브랜치
  • release: master로의 merge를 위한 준비
  • hotfix: master의 버그 픽스 후 develop과 master로 빠른 수정
  • master: 프로덕션 브랜치

image from hexo

참고

WAS 세팅 및 튜닝

프론트앤드

Virtual DOM

DOM 조작의 실제 문제는 각 조작이 레이아웃 변화, 트리 변화와 렌더링을 일으킨다는겁니다.

Virtual DOM 은 변화가 일어나면 그걸 오프라인 DOM 트리에 적용시키죠. 이 DOM 트리는 렌더링도 되지 않기때문에 연산 비용이 적어요. 연산이 끝나고나면 그 최종적인 변화를 실제 DOM 에 던져주는거에요. 딱 한번만 한는거에요.

사실, 이 과정은 Virtual DOM 이 없이도 이뤄질수 있어요. 그냥, 변화가 있을 때, 그 변화를 묶어서 DOM fragment 에 적용한 다음에 기존 DOM 에 던져주면 돼요.

그러면, Virtual DOM 이 해결 하려고 하는건 무엇이냐? 그 DOM fragment를 관리하는 과정을 수동으로 하나하나 작업 할 필요 없이, 자동화하고 추상화하는거에요. 그 뿐만 아니라, 만약에 이 작업을 여러분들이 직접 한다면, 기존 값 중 어떤게 바뀌었고 어떤게 바뀌지 않았는지 계속 파악하고 있어야하는데 (그렇지 않으면 수정 할 필요가 없는 DOM 트리도 업데이트를 하게 될 수도 있으니까요), 이것도 Virtual DOM 이 이걸 자동으로 해주는거에요. 어떤게 바뀌었는지 , 어떤게 바뀌지 않았는지 알아내주죠.

마지막으로, DOM 관리를 Virtual DOM 이 하도록 함으로써, 컴포넌트가 DOM 조작 요청을 할 때 다른 컴포넌트들과 상호작용을 하지 않아도 되고, 특정 DOM 을 조작할 것 이라던지, 이미 조작했다던지에 대한 정보를 공유 할 필요가 없습니다. 즉, 각 변화들의 동기화 작업을 거치지 않으면서도 모든 작업을 하나로 묶어줄 수 있다는거죠.

출처

ES6

SEO

Angular

React

Vue

Window에서 aws cli 사용시 bad interperter 오류

· One min read

git bash에서 aws cli 명령어를 실행시에 c:\Program Files... bad interpreter 오류가 나오면서 실행이 안 될 때는 다음과 같이 하면 된다.

~/.bash_profile 에 aws alias를 추가한다.

$ vi ~/.bash_profile
# aws script 경로를 강제로 지정한다
alias aws='python "C:\Program Files\Python\Scripts\aws"'
# 추가 후 저장
$ source ~/.bash_profile

Cloudfront 캐시 지우기

· One min read

클라우드프론트로 static website hosting 을 할 경우에 삭제된 s3 의 파일이 노출되는 경우가 있다. 캐싱처리되어서 보이는 현상인데 다음과 같이 해결하면 된다.

Invalidate 기능을 사용해서 캐시를 지워버리자. Cloudfront Distributions > ID 클릭 > Invalidations 에서 Create Invalidation 버튼을 누른다.

image from hexo

Object Paths에 캐시를 지울 경로를 입력한다. 예를 들어 assets/js 하위의 소스 맵 파일만 지운다면 다음과 같다.

/assets/js/*.map

입력 후에 Invalidate 버튼을 누르면 5~10 분 안에 리스트의 Status 가 Completed 로 변하고 기존 캐시가 사라진다.