FE 전배

준비 & 적응 후기.

프론트엔드 개발, 이상진 😶

입사 : UI 개발

전배 : 프론트앤드 개발

- 블로그

- Selective

- 지식인

- 플레이스 (파견)

- A&B 테스팅 툴 (사내 Pasta 플랫폼)

- 자동차 보험

- 엔트리

- 현: 판매자 센터

2017.03

2020.03

Work Dialog.

- 인플루언서

1. 전배를 생각하게 된 계기

2. 준비 과정

3. 전배 후 1년

4. 후기 및 느낀 점

5. Q&A

Index.

1.

전배를 생각하게 된 계기

1.  새로운 작업 환경

AS-IS

HTML/CSS DIFF 전달

FE와 분리된 환경

순수한 HTML/CSS 파일

FE 코드 이해 필요 x

TO-BE

JSX 형식의 파일

UIT 리엑트 마크업 가이드 RND

AS-IS

HTML/CSS DIFF 전달

TO-BE

FE팀과 분리된 환경

순수한 HTML/CSS 파일

FE 코드 이해 필요 x

JSX 파일에 함께 작업

FE팀과 같은 저장소

FE 코드 이해 필요 o

import React from 'react';
import style from './TodoList.scss';

const DEMO_PROPS = {
  itemList: [
    { id: 1, todo: '투두리스트 컴포넌트 실습 완성하기' },
    { id: 2, todo: '투두리스트 컴포넌트 실습 복습하기 ' },
    { id: 3, todo: '새로운 내용 공부하기' },
  ]
};

class TodoList extends React.Component {
  render () {
    const { itemList } = DEMO_PROPS;

    return (
      <ul className={style.list_todo}>
          {itemList.map((item) => (
            <li className={style.item_todo} key={item.id}>
                <span className={style.text}>{item.todo}</span>
                <button 
                  type="button" 
                  className={style.button_delete} 
                  aria-label="삭제"
                >
                  <i className={style.icon_delete} />
                </button>
            </li>
          ))}
      </ul>
    )
  }
}

export default TodoList;

최초 작업 > 데모 데이터

import React from 'react';
import { useSelector } from "react-redux";
import { getTodos } from "../redux/selectors";
import { VISIBILITY_FILTERS } from "../constants";
import TodoItem from "./TodoItem";
import style from "./TodoList.scss";

const TodoList = () => {
  const visibilityFilter = useSelector((state) => state.visibilityFilter);
  const allTodos = useSelector(getTodos);
  const itemList =
    visibilityFilter === VISIBILITY_FILTERS.ALL
      ? allTodos
      : visibilityFilter === VISIBILITY_FILTERS.COMPLETED
      ? allTodos.filter((todo) => todo.completed)
      : allTodos.filter((todo) => !todo.completed);

  if (itemList.length === 0) {
    return <h2>등록된 Todo가 없습니다.</h2>;
  }

  return (
    <ul className={style.list_todo}>
      {itemList.map((todo) => {
        return (
          <TodoItem 
            key={`todoItem-${todo.id}`} 
            todo={todo} 
          />
        );
      })}
    </ul>
  );
};

export default TodoList;

개발 완료 > 리얼 데이터 + 로직

FE 개발 이후
추가 작업

FE 작업의 일부분 수행하는 케이스

간단한 이벤트 구현
(ex: 툴팁, 팝업 Toggle)

const [isShowLayer, setIsShowLayer] = useState(false);

function handleClickCancel(e) {
    setShowLayer(false)
}

...

return (
    {isShowLayer && (
        <TooltipLayer onClose={handleClickCancel} /> 
    )}
 )
 

상위 컴포넌트에 있는 리얼 데이터 적용

const ChildComponent = () => {
  const DEMO_PROPS = {
    name: 'jack',
    id: 'ads21',
    imgUrl: 'https://via.placeholder.com/728x90.png',
    desc: 'dummy desc text!!'
  }
  const { name, id, imgUrl, desc } = DEMO_PROPS;

  return (
    <div>
    	<div>{name}</div>
    	<div>{id}</div>
    	<img src={imgUrl} />
    	<p>{desc}</p>
    </div>
  );
}
const ChildComponent = (data) => {
  const { name, id, imgUrl, desc } = data;

  return (
    <div>
    	<div>{name}</div>
    	<div>{id}</div>
    	<img src={imgUrl} />
    	<p>{desc}</p>
    </div>
  );
}

After

Before

리얼 데이터 관련

코드 확인 필요한 경우

- 예상치 못한 데이터로 인해 UI 이슈 발생 / 기존 스펙 확인 필요

 개발 코드 직접 확인

 커뮤니케이션 비용 발생

예시

import React from 'react'
import { Link } from 'react-router-dom';

const MovieItem = ({ data }) => {
  const { id: movieId, name: movieName, platform, slug, poster } = data;

  return (
    <div className="movie-item" key={movieId}> 
      <Link to={`/detail/${platform}/${slug}`} className="movie-item__drop">
        <img className="movie-item__img" src={poster} alt={movieName} loading="lazy"/>
      </Link>
      <div className="movie-item__name">{movieName}</div>
    </div>
  )
}

export default MovieItem;

MovieItem.jsx

import React from 'react'
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';
import MovieItem from './MovieItem';
import LoadingUI from './LoadingUI';

const movieList = ({ movies }) => {
  const { movieList = [] } = movies;

  return (
    <div className="movie pt-5"> 
      <div className="d-flex align-items-center justify-content-between mb-4">
        <h2 className="">FİLMLER</h2>
        <Link to="/add/movie" className="movie-add yellow">Film Ekle</Link>
      </div>
      {movieList?.length === 0 && <LoadingUI />}
      <div className="row">
        {movieList.map((movie, index) => (
            <MovieItem
              key={`movieItem${index}`}
              data={movie} 
            />
        ))}
      </div>
    </div>
  )
}

movieList.propTypes = {
  movies: PropTypes.object.isRequired
};

export default movieList;

MovieList.jsx

import React, { PureComponent } from "react";
import propTypes from "prop-types";
import { connect } from "react-redux";
import GameList from "../components/gameList.jsx";
import MovieList from "../components/movieList.jsx";

import { fetchMovies } from "../actions/movies";
import { clearDetail, fetchGames } from "../actions/games";
import { searchDisableOverlay  } from "../actions/search";

class moviesPage extends PureComponent {
  static propTypes = {
    movies: propTypes.object.isRequired, 
    games: propTypes.object.isRequired,
  };

  render() {
    return (
      <div>
        <div 
          className={`search-overlay ${this.props.boolSearchOverlay === "true" ? "active" : ""}`} 
          onClick={this.closeOverlay}
        />
        <div className="custom-container product">
          <GameList games={this.props.games} />
          <MovieList movies={this.props.movies} />
        </div>
      </div>
    );
  }

  componentDidMount() {
    this.props.fetchMovies(); //action
    this.props.fetchGames(); //action
    this.props.clearDetail(); //action detay state temizle
  }

  componentWillUnmount(){
    this.props.searchDisableOverlay();
  }

  closeOverlay = () => {
    this.props.searchDisableOverlay();
  }
}

const mapStateToProps = (state) => {
  return {
    movies: state.movies,
    games: state.games,
    boolSearchOverlay: state.search.searchOverlay
  };
};

const mapDispatchToProps = {
  fetchMovies,
  fetchGames,
  clearDetail,
  searchDisableOverlay
};

export default connect(mapStateToProps, mapDispatchToProps)(moviesPage);

ProductPage.jsx

import React, { PureComponent } from "react";
import propTypes from "prop-types";
import { connect } from "react-redux";
import GameList from "../components/gameList.jsx";
import MovieList from "../components/movieList.jsx";

import { fetchMovies } from "../actions/movies";
import { clearDetail, fetchGames } from "../actions/games";
import { searchDisableOverlay  } from "../actions/search";

class moviesPage extends PureComponent {
  static propTypes = {
    movies: propTypes.object.isRequired, 
    games: propTypes.object.isRequired,
  };

  render() {
    return (
      <div>
        <div 
          className={`search-overlay ${this.props.boolSearchOverlay === "true" ? "active" : ""}`} 
          onClick={this.closeOverlay}
        />
        <div className="custom-container product">
          <GameList games={this.props.games} />
          <MovieList movies={this.props.movies} />
        </div>
      </div>
    );
  }

  componentDidMount() {
    this.props.fetchMovies(); //action
    this.props.fetchGames(); //action
    this.props.clearDetail(); //action detay state temizle
  }

  componentWillUnmount(){
    this.props.searchDisableOverlay();
  }

  closeOverlay = () => {
    this.props.searchDisableOverlay();
  }
}

const mapStateToProps = (state) => {
  return {
    movies: state.movies,
    games: state.games,
    boolSearchOverlay: state.search.searchOverlay
  };
};

const mapDispatchToProps = {
  fetchMovies,
  fetchGames,
  clearDetail,
  searchDisableOverlay
};

export default connect(mapStateToProps, mapDispatchToProps)(moviesPage);

ProductPage.jsx

해당 데이터는 Redux 관련
→ 코드 읽기 위해선 학습 필요 📚

JSX 문법

/

React

/

Javascript

/

Redux

필요에 의한 학습

a. 모르는 부분 학습

b. 읽을 수 있는 코드 확장

c. 작업 범위 확장

[실무] + [필요] 에 의한 학습

FE 개발자로 일해보는건 어떨까? 🤔

→ 지루하지 않음, 마크업과 다른 종류의 즐거움

2.

전배 준비 과정 🔥

1. 호기로운 시작

+

그 외 인터넷 강좌들

1. 호기로운 시작

2. 좌절

좁혀보자!

필요한 부분 부터

1. FE 개발자 로드맵

2. 전배 테스트

3. 토이 프로젝트

1. FE 개발자 로드맵

학습이 필요한 주제를 우선 순위에 맞추어

효율적인 "학습 순서"를 제시

1. 구현 요건

2. 평가 기준 리스트 → 채점표

- 내 현재 실력 확인

- FE업무에 필요한 실무 능력 파악

- 학습 방향 설정 가능

2. 전배 테스트

출제되는 과제를 요구 조건에 맞춰 구현하는 방식

있으면 좋을 것 같은 웹서비스 구현

or

존재하고 있는 서비스 Clone 코딩으로 구현

3. 토이 프로젝트

🤖 토이 프로젝트란?

ex) 넷플릭스, 코로나 현황판

1. 구현 요건 정의(=기획서)

작업 전 해야 할 것

작업 전 해야 할 것

2. 기술 스택 정의

프레임워크 번들러 상태 관리 타입스크립트 테스팅
Vue webpack Redux O Jest

FE

프레임워크 DB
Express MySQL

 BE (optional) 

작업 전 해야 할 것

3. 아키텍쳐 (mvc, mvvm, mvp 등)

프레임워크 없이 순수 JS (vailla JS)로 구현시

mvc 패턴

mvvm 패턴

mvp 패턴

  • Todo 리스트
  • Todo + 캘린더
  • Hackers News 클론 코딩
  • 정렬 알고리즘 시각화
  • 워드 클라우드
  • 서점 서비스
  • world news 서비스
  • 위치기반 장소 예약 서비스

진행한 토이 프로젝트들

1. 정렬 알고리즘 시각화

2. world news 서비스

📌 제가 생각하는 Tips

- 공부의 우선 순위를 정하기

- app 만들어보기 실무에 가까운 공부 

- 자바스크립트

- 실무에 필요한 부분 찾아서 공부

- ES6 이상의 문법 권장

- 상태관리 & 비동기 처리

- 프레임워크, 라이브러리에 의존하지 않기

3.

전배, 그 후 1년

다양한 환경의 업무 경험

A/B 테스트 툴

2020.03

자동차 보험

엔트리

현: 판매자센터(스마트스토어/네이버 페이)

현: 판매자센터(스마트스토어/네이버 페이)

A/B 테스팅 관리자 툴

신규 프로젝트

A/B 테스팅 관리자 툴

A 화면

B 화면

A/B 테스팅 관리자 툴

A 화면

B 화면

기획, 디자이너 x 

마크업 함께 진행 (UI 키트 이용)

특징

Past UI Kit: https://pages.oss.navercorp.com/paas/ui-component

- 원하는 기술 스택 적용 가능

- 웹팩 설정 ~ 최종 빌드 산출물

→  번들러, 개발 환경 구축 경험

→  필요에 따라 자유롭게 기술 추가

특징 : 신규프로젝트

app_bundle.js

Context API

판매자 센터

기존 프로젝트 이관

→ 운영 / 유지 / 보수

특징

1. 최신 기술 스택, 고도화된 개발 환경

프레임워크 번들러 GraphQL 타입스크립트
React+hook webpack O O
스토리북 테스트 프레임워크 스냅샷 테스트 e2e 테스트
O Jest O O

특징

2. 복잡한 인프라

특징

3. 서비스 운영 & 배포

4.

총평 및 느낀 점 🤔

느낀점 - 좋았던 점

1. Refresh

느낀점 - 좋았던 점

2. 다른 종류의 즐거움

느낀점 - 좋았던 점

3. UI 개발 경험은 큰 자산

- 마크업 관련 작업 혹은 이슈 처리

- 인터렉션, 애니메이션 작업

- HTML, CSS를 이용, 상대적으로 간단하게 개발 가능

의존성 감소 → 업무 시 이점

- 성능 최적화 작업

1. 넓어진 업무 범위, 높은 의존성

느낀점 - 힘든점

느낀점 - 힘든점

- 어려운 BackEnd 와 의사소통

- 여러 부서와 협업 필요

→ BE에 대한 학습이 필요 

→ 가장 마지막에 작업

→ 일정 관리 어려움

1. 넓어진 업무 범위, 높은 의존성

2. 끝없는 학습의 필요성

느낀점 - 힘든점

- JS : Ecma, 다양한 프레임워크, 상태관리 lib

- 보조&인프라: Typescript, 스토리북, 테스팅

- 배포, 운영 : 람다, ncc, nelo 등

"필요할 때 공부하자"

느낀점 - 힘든점

- BE : GraphQL, nodejs, http 통신 등

2. 끝없는 학습의 필요성

4.

Q&A

감사합니다 😊 

FE 전배

By Sang Jin Lee

FE 전배

  • 328