2월 25일(금) 면접 질문 정리 (AI 스타일링)

Southbig·2022년 2월 25일
0

SPA (Single-Page Application)

서버로부터 완전한 새로운 페이지를 불러오지 않고 페이지 갱신에 필요한 데이터만 받아 그 정보를 기준으로 현재 페이지를 업데이트함으로써 사용자와 소통하는 웹어플리케이션이나 웹사이트

기존방식과 SPA 방식의 차이

기존방식의 웹에서는 페이지 이동을 하고 서버에 요청을 하면 서버가 HTML 파일을 만들어서 클라이어트에 보내주고 브라우저가 HTML을 반영해서 보여주기 전 새로고침이 일어난다

SPA에서는 페이지 이동시 서버에 AJAX로 요청하고 Server는 JSON만 전달해 준다
SPA 방식에서는 페이지 이동시 변경되는 부분만 JSON으로 받아온뒤 브라우저의 자바스크립트에서 받은 JSON을 토대로 DOM에 변경된 부분만 랜더링 해준다

SPA는 HTML 랜더링을 서버가 하는게 아닌 클라이언트가 받은 json을 토대로 하기 때문에 CSR 방식으로 랜더링 한다고 말한다

즉,
기존엔 브라우저가 페이지를 보여주기 위해선 HTML파일로 된 페이지 전체를 불러와야 했다.
매번 모든 페이지를 불러오는 거에 따른 느린 반응성을 갖게 된다

SPA는 서버로부터 완전히 새로운 페이지를 불러오는 것이 아닌, 화면을 업데이트하기 위해 필요한 데이터만 서버에서 전달 받아 브라우저에서 해당하는 부분만 업데이트하는 방식으로 빠른 반응성을 가진다

SPA 장점

  • 전체페이지가 아니라 필요한 부분의 데이터만 받아서 화면을 업데이트하면 되기 때문에 사용자의 interaction에 빠르게 대응하고, 더 나은 유저경험을 제공한다
  • 서버에서는 요청 받은 데이터만 넘겨주면되기 때문에 서버 과부하 문제가 현저하게 줄어든다

SPA 단점

  • 자바스크립트 파일을 기다리는 시간으로 첫 화면 로딩 시간이 길다
    SPA는 HTML파일은 거의 비여있고 자바스크립트를 이용하여 동적으로 HTML 요소를 생성하는 방식으로 자바스크립트가 모든일은 한다, 자바스크립트 파일이 커서 자바스크립트 파일을 기다리는 시간으로 첫 화면 로딩 시간이 길어진다
  • 검색 엔진 최적화에 좋지 않다
    SPA는 HTML이 거의 비어있어서 검색 로봇이 충분한 자료를 수집하지 못한다 (검색 엔진은 HTML 파일에 있는 자료를 분석하는 방식으로 검색 기능을 구동한다 / 하지만 SPA에서도 검색 엔진 최적화에 대응할 수 있도록 검색 엔진이 발전하고 있어서 점차 이 단점은 사라지고 있는 추세이다)

AJAX (Asynchronous JavaScript + XML(AJAX))
Ajax는 빠르게 동작하는 동적인 웹 페이지를 만들기 위한 개발 기법의 하나다
Ajax는 웹 페이지 전체를 다시 로딩하지 않고도, 웹 페이지의 일부분만을 갱신할 수 있다

MPA (Multiple-Page Application)

사용자가 페이지를 요청할 때 마다 웹서버에서 요청한 UI와 필요한 HTML로 파싱해서 보여주는 방식

  • 여러 개의 페이지로 구성된 Application
  • MPA는 SSR(Server Side Application) 방식으로 랜더링 한다
    새로운 페이지를 요청할 때마다 서버에서 랜더링된 정적 리소스(HTML, CSS, JS)가 다운로드 된다
    페이지 이동하거나 새로고침하면 전체 페이지를 다시 랜더링 한다

MPA의 장점

SEO 관점에서 유리하다
MPA는 완정된 형태의 HTML 파일을 서버로부터 전달 받는다, 검색엔진이 페이지를 크롤링하기에 적합하다

첫 로딩이 매우 짧다
서버에서 이미 랜더링해 가져오기 때문에 첫 로딩이 빠르다
(클라이언트가 JS 파일을 모두 다운로드하고 적용하기 전 까지는 각각의 기능은 동작하지 않는다)

MPA의 단점

새로운 페이지로 이동하면 깜빡인다
매 페이지 요청마다 리로딩(새로고침) 발생

페이지 이동시 불필요한 템플릿도 중복해서 로딩 (성능)
서버 랜더링에 따른 부하

git merge와 rebase 차이

merge는 branch를 통합하는 것, rebase는 branch의 base를 옮긴다는 개념

ex

B 지점을 base로 가진 branch가 D, E 커밋을 진행 한다
C 지점으로 base를 이동하기 위해 branch에서 C 지점으로 rebase를 한다
C 지점으로 rebase 되면 기존 D, E 커밋은 새롭게 정렬되어 C 지점 이후로 변경된다

redux 종류 (saga, thunk)

Redux-thunk와 Redux-saga는 둘 다 Redux의 미들웨어 라이브러리이다

Redux 미들웨어는 dispatch()메소드를 통해 store로 가는 액션을 가로채는 코드

Redux-thunk

리덕스를 사용하는 어플리케이션에서 비동기 작업을 처리 할 때 가장 기본적인 방법으로 redux-thunk 미들웨어를 사용한다

redux-thunk를 사용하여 비동기 작업을 관리하는건 매우 직관적이고 간단하다

thunk 란?

특정 작업을 나중에 하도록 미루기 위해서 함수형태로 감싼 것

ex

const x = 1 + 2;

위 코드는 1+2 연산이 바로 실행된다

const foo = () => 1 + 2;

위 코드는 foo() 함수가 호출되어야만 1 + 2 연산이 실행된다

redux-thunk 미들웨어는 객체 대신 함수를 생성하는 액션 생성함수를 작성 할 수 있게 해준다

redux는 기본적으로 액션 객체를 디스패치 한다

일반 액션 생성자

const actionCreator = (payload) => ({ action: 'ACTION', payload });

위와 같이 파라미터를 가지고 액션 객체를 생성하는 작업만 한다

만약 특정 액션이 몇초뒤에 실행되게 하거나, 현재 상태에 따라 액션이 무시되게 하려면 위와 같은 일반 액션 생성자로는 할 수 없다, 하지만 redux-thunk는 가능하다

1초뒤에 액션이 디스패치되게하는 코드

const INCREMENT_COUNTER = 'INCREMENT_COUNTER';

function increment() {
  return {
    type: INCREMENT_COUNTER
  };
}

function incrementAsync() {
  return dispatch => { // dispatch를 파라미터로 가지는 함수를 리턴
    setTimeout(() => {  // 1초뒤 dispatch 실행
      dispatch(increment());
    }, 1000);
  };
}

위와 같이 코드를 짜고 store.dispatch(incrementAsync()); 를 하면 INCREMENT_COUNTER 액션이 1초뒤에 디스패치 된다

액션을 디스패치 하거나 무시하는 코드

function incrementIfOdd() {
  return (dispatch, getState) => {
    const { counter } = getState();
      if(counter % 2 === 0) {
        return;
      }
    dispatch(increment());
  };
}

위와 같이 리턴하는 함수에 dispatch, getState를 파라미터로 받게 하면 스토어의 상태에도 접근 할 수 있다
현재의 스토어 상태의 값에 따라 액션이 dispatch 될 지 무시될지 정해줄 수 있다

redux-thunk는 일반 액션 생선자를 통제, 제어할 수 있게 해준다
보통 액션생성자는 그냥 하나의 액션 객체를 생성 할 뿐이지만,
redux-thunk를 통해 만든 액션 생성자는 그 내부에서 여러가지 작업을 할 수 있다
예를들어 네트워크 요청도, 액션을 여러번 디스패치 할 수도 있다

dispatch, getState는 어디서 오는 걸까 ?

redux-thunk 미들웨어에서, 전달받은 액션이 함수 형태 일 때, 그 함수에 dispatch 와 getState 를 넣어서 실행해준다

예제 코드

function createThunkMiddleWare(extraArgument) {
  return ({ dispatch, getState }) => next => action => {
    if(typeof action === 'function') {
      retrun action(dispatch, getState, extraArgument);
    }
    return next(action);
  };
}

const thunk = createThunkMiddleWare();
thunk.withExtraArgument = createThunkMiddleWare;

export default thunk;

// 이해가 가질 않는다 ;

설치, 적용

설치

$ yarn add redux-thunk

스토어에 미들웨어 적용 // src/store.js

import { createStore, applyMiddleware } from 'redux';
import modules from './modules';

import { createLogger } from 'redux-logger';
import ReduxThunk from 'redux-thunk';

/* 로그 미들웨어를 생성 할 때 설정을 커스터마이징 할 수 있습니다.
   https://github.com/evgenyrodionov/redux-logger#options
*/
const logger = createLogger(); 

const store = createStore(modules, applyMiddleware(logger, ReduxThunk))

export default store;

일반 redux store // src/store.js

import { compose, createStore, applyMiddleware } from "redux";
import rootReducer from '../reducers/index';
import thunk from "redux-thunk";

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
  : compose;

const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)));

export default store;

redux-thunk 사용 예제 코드

src/modunles/counter.js

import { handleActions, createAction } from 'redux-actions';

const INCREMENT = 'INCREMENT';
const DECREMENT = 'DECREMENT';

export const increment = createAction(INCREMENT);
export const decrement = createAction(DECREMENT);


export const incrementAsync = () => dispatch => {
    // 1초 뒤 액션 디스패치
    setTimeout(
        () => { dispatch(increment()) },
        1000
    );
}

export const decrementAsync = () => dispatch => {
    // 1초 뒤 액션 디스패치
    setTimeout(
        () => { dispatch(decrement()) },
        1000
    );
}

export default handleActions({
    [INCREMENT]: (state, action) => state + 1,
    [DECREMENT]: (state, action) => state - 1
}, 0);

App 컴포넌트에서 increment -> incrementAsync, decrement -> decrementAsync 로 치환

import React, { Component } from 'react';
import { bindActionCreators } from 'redux';
import { connect } from 'react-redux';
import * as counterActions from './modules/counter';


class App extends Component {
    render() {
        const { CounterActions, number } = this.props;

        return (
            <div>
                <h1>{number}</h1>
                <button onClick={CounterActions.incrementAsync}>+</button>
                <button onClick={CounterActions.decrementAsync}>-</button>
            </div>
        );
    }
}

export default connect(
    (state) => ({
        number: state.counter
    }),
    (dispatch) => ({
        CounterActions: bindActionCreators(counterActions, dispatch)
    })
)(App);

추가 내용

redux-actions

yarn add redux-actions
  • 액션 생성 함수를 더 짧은 코드로 작성할 수 있게 해준다
  • 리듀서를 작성할 때 switch문이 아닌 handleActions라는 함수를 사용할 수 있게 해준다

createAction

  • 액션 생성 함수를 만들어주는 함수이다
  • 직접 객체를 만들 필요 없어 훨씬 간단하다
// createAction을 사용하지 않을 때
const CHANGE_USER = 'user/CHANGE_USER'; // 액션 생성 함수 
export const change_user = user => ({type: CHANGE_USER, user});
// createAction을 사용할 때
import { createAction } from 'redux-actions'; 
const CHANGE_USER = 'user/CHANGE_USER'; // 액션 생성 함수 
export const change_user = createAction(CHANGE_USER, user => user);

handleActions

handleActions로 리듀서를 더 간단하게 작성할 수 있다

// handleActions를 사용하지 않을 때
const reducer = (state = initState, action) => { switch (action.type) { case CHANGE_USER: return { ...state, user: action.user } } }
// handleActions를 사용할 때
import { handleActions } from 'redux-actions'; 
const reducer = handleActions({ [CHANGE_USER]: (state, action) => ({...state, user: action.user}) });

Redux-saga

redux-saga는 비동기작업처럼 리듀서에서 처리하면 안되는 순수하지 않은 작업들을 하기위해 사용한다

redux-thunk의 경우 함수를 dispatch 해주었고, redux-promise-middleware 나 redux-pender에선 promise가 들어있는 액션을 dispatch 해주었다면,
redux-saga에서는 일반 액션을 dispatch 하게 된다

redux-saga는 특정 액션이 발생하면 이를 모니터링 하여 이에 기반하여 추가적인 작업을 하도록 설계한다

redux-saga에서는 Generator를 사용하여 function* 같은 문법을 사용한다

설치

yarn add redux-saga

redux-saga 미들웨어 적용

redux-saga 미들웨어 적용 //src/store.js

import { createStore, applyMiddleware } from 'redux';
import { createLogger } from 'redux-logger';
import createSagaMiddleware from 'redux-saga';
import modules from './modules';
const logger = createLogger();

const sagaMiddleware = createSagaMiddleware();

const store = createStore(modules, applyMiddleware(logger, sagaMiddleware));

export default store;

일반 redux store // src/store.js

import { compose, createStore, applyMiddleware } from "redux";
import rootReducer from '../reducers/index';
import thunk from "redux-thunk";

const composeEnhancers = window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__ ?
  window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({})
  : compose;

const store = createStore(rootReducer, composeEnhancers(applyMiddleware(thunk)));

export default store;

즉,

  • redux-saga 미들웨어를 사용하면 복잡한 애플리케이션 로직을 sagas라는 순수함수로 표현 할 수 있다
    (순수함수는 예측 가능하고 반복 가능하기 때문에 테스트 관점에서 바람직하고, 상대적으로 테스트하기 쉽다)
  • sagas는 Generagor라는 특별 함수를 통해 구현된다
    (ES6의 새로운 기능인 generator함수는 기본적으로, yield문이 있는 모든 곳에서 generator 안팎으로 이동한다)
    이 문법의 핵심 기능은 함수를 작성 할 때 함수를 특정 구간에 멈춰놓을 수도, 원할 때 다시 돌아가게 할 수도 있고, 결과값을 여러번 반환 할 수도 있다

어려운 개념으로 추가 정리 필요

redux-thunk 와 redux-saga의 차이

Thunks는 action에 응답을 줄 수 없다

반면 Redux-Saga는 스토어를 구독하고 특정 액션이 전달 될 때 saga를 실행하거나 계속할 수 있다

redux-saga
redux-saga로 비동기처리와 분투하다

참고 사이트

promise와 async await 차이

  1. 에러핸들링
  • promise를 사용하면 .catch()문을 통해 에러 핸들링이 가능하다
  • async/await는 에러 핸들링 할 수 있는 기능이 없어 try-catch() 문을 활용해야한다
  1. 코드 가독성
  • promise도 코드가 길어지면 callback 지옥처럼 .then 지옥의 가능성이 있다
  • 코드가 길다면 async/await를 활용한 코드가 가독성이 좋다
  • async/await는 비동기 함수를 마치 동기적인 함수처럼 쓸 수가 있다
    (코드 흐름을 이해하기 쉽다)

CSS 캐스케이딩

스타일 적용 우선순위

ex

<html> 
  <head> 
    <meta charset="UTF-8"> 
      <style> 
      li{color:blue;} #apple{color:red;} .greenapple{color:green;} 
      </style> 
    </head> 
    <body> 
      <ul> 
        <li>포도</li> 
        <li id ="apple" class ="greenapple" style=" color:yellow">사과</li> 
        <li>수박</li> 
      </ul> 
</html>

id(빨간색) / class(초록색) / style(노란색) / li(파란색)

우선순위 : style 선택자 > id 선택자 > class선택자 > li(태그)선택자
태그 선택자의 경우 다른 태그인 <ul> / <p> / <div>... 등 여러 태그를 의미한다

CSS 우선순위

  1. 속성 값 뒤에 !important 를 붙인 속성
  2. HTML에서 style을 직접 지정한 속성
  3. #id 로 지정한 속성
  4. .클래스, :추상클래스 로 지정한 속성
  5. 태그이름 으로 지정한 속성
  6. 상위 객체에 의해 상속된 속성

react 컴포넌트, 컨테이너

Dumb component - 부모에서 내려온 props를 받아쓰는 컴포넌트
Smart component / Container - 리덕스와 소통하면서 앱의 상태(리덕스 state)를 제어하는 컴포넌트

리덕스를 사용할 때에는 Container와 Component를 구분해주는 게 좋다

Container는 앱의 상태를 관리하기 때문에 앱의 상태가 자주 바뀔수록 그에 따라 빈번하게 업데이트가 일어난다

필요없는 부분에 업데이트가 일어나지 않게 하려면 Container과 Component를 구분해주어야한다

react는 컴포넌트 단위로 업데이트한다
Container가 업데이트되어도 그 아래 Component와 상관이 없다면 업데이트가 일어나지않는다

Presentational 컴포넌트

  • 데이터 출력, 데이터 처리 능력은 없음, no logic
  • DOM 마크업과 스타일 담당(UI)
  • Redux와 관련 없음
  • 부모 컴포넌트로부터 받은 Props인 데이터와 콜백(callback)을 사용

Container 컴포넌트

  • 데이터 처리 능력 있음, 동작(behavior) logic, API Request, Exception Error, setState... ETC ...
  • Redux와 관련 있음
  • 렌더링 되어야 할 데이터를 props로써 데이터 처리 능력이 없는 Presentational 컴포넌트로 전달

jwt, token 저장 방식

프로젝트 과정에서 jwt token은 모두 쿠키에 저장

localStorage에 저장

장점

  • CSRF 공격에 안전하다
    request에 담기는 쿠키와는 다르게 js코드에 의해 헤더에 담기므로 xss를 뚫지 않는 이상 공격자가 정상적인 사용자인 척 request를 보내기가 어렵다

단점

  • XSS에 취약하다
    공격자가 localStorage에 접근하는 js 코드 한 줄만 주입하면 localStorage를 공격자가 내집처럼 드나들 수 있다

cookie에 저장

장점

  • XSS 공격으로부터 localStorage에 비해 안전하다
    쿠키의 httpOnly 옵션을 사용하면 js에서 쿠키에 접근 자체가 불가능하다
    XSS 공격으로 쿠키 정보를 탈취할 수 없다
    (httpOnly 옵션은 서버에서 설정할 수 있다)
    하지만 XSS 공격으로 부터 완전히 안전하지는 않다
    httpOnly 옵션으로 쿠키의 내용을 볼 수 없다해도
    js로 request를 보낼 수 있으므로 자동으로 request에 실리는 쿠키의 특성 상 사용자의 컴퓨터에서 요청을 위조할 수 있기 때문이다

단점

  • CSRF 공격에 취약하다
    자동으로 http request에 담아서 보내기 때문에
    공격자가 request url만 안다면 사용자가 관련 link를 클릭하도록 유도하여 request를 위조하기 쉽다

추가정리

profile
즐겁게 살자

0개의 댓글