실습 2달만에 혼자 사내 프로젝트 완수한 썰

미진·2023년 12월 18일
58
post-thumbnail
post-custom-banner

"미진씨, 이거 필요할 것 같아요"

실습 온지 3일도 안되어서 들은 말. 그리고 뒤로 이어지는 많은 설명들 ...

한 줄로 요약하자면, CSV데이터 파일을 HxD(Hex Editor)가 읽어들일 수 있는 웹프로그램이 필요하다고 말씀을 하셨다. 문제를 파악해보니, 현재 만들어진 제품에 들어가는 데이터 파일을 자동으로 매핑해주는 시스템이 필요한 것이였다. 원래 역할이 다 분리 되어있지만, 실습생을 처음 받는 팀이기도 했기에 나(*참고로 필자는 고등학생이다.)에게 어느 정도의 역량이 있는지 확인해보시고 싶으셨던건지 그냥 온전히 내게 다 맡기셨다. 😵‍💫

뭐부터 해야하지 ...

진짜 말그대로 나는 무(無)의 상태였기에 어떻게 해야할지 잠깐 막막했지만, 학교에서 경험 해본 것들을 되집어봤다. 그리고 이를 토대로 기획서를 작성했다. (학교에서 배운것들이 빛을 발하는 순간 ... ⭐️) 나오기전에 기획서 작성법, WBS나 피그마로 디자인하는 방법 등 배운 것들을 써먹을 수 있는 기회가 생겨 수업을 잘 들어서 다행이라 생각했다.

따로 양식도 안 주셨기 때문에 노션을 활용하여 작성을 했다.

개요나 목적, 스택, 기능 명세 기본적인 것들을 작성해주고

일정 관리를 하기 위해 WBS도 작성해주었다. 이건 여담이지만, 선임님께서 WBS까지 작성할 줄 예상 못하셨는데 기간도 적당히 잘 잡아서 작성했다고 칭찬해주셨다!

보안상의 이유로 전체 이미지를 보여줄 수는 없지만, 이렇게 서비스 흐름 및 구성도도 작성해주었다. 그리고 이건 꿀팁인데 파일은 항상 2가지 방식을 export하는 걸 추천한다. 발표하다가 파일이 안 열리면 곤란할 수 있고 이렇게 하면 원래 파일 형식이 잘못되더라도 금방 대처할 수 있다. 이 부분도 학교에서 제출할때 항상 파일을 두 가지 형식을 요청해서 생긴 습관이다.

회사에서 기획하고 발표를 하는게 처음이라 욕먹지만 않으면 다행이라고 생각했는데 오히려 잘 했다고 칭찬까지 해주시니 너무 뿌듯했다. 원래 요청했던 부분에 추가한 나의 의견도 반영해서 기획했는데 좋게 봐주셔서 넣을 수 있게 되었다.

확장성을 고려해 디자인하기

기획했다고 끝이 아니다. 이제 디자인도 해야 한다. 프론트엔드 개발자임에도 불구하고 난 디자인 역량이 부족한 편이다. 그래도 피그마나 어도비 같은 디자인 툴을 배운적 있어서 이를 최대한 활용하기 위해 노력했다.

보여지는 화면이 많이 없기 때문에 간단하게 할까 생각했지만, 나중에 화면이 추가되거나 수정이 쉽도록 디자인 시스템을 적용하기로 했다.

색상은 Open Color를 참고했고, 폰트는 Spoqa Han Sans Neo를 적용하였다.

하나 하나 색상을 적고 등록하는 것이 지금 당장은 귀찮더라도 나중에는 편하게 쓸 수 있을테니 만드는 걸 추천한다.

디자인의 극히 일부다. 디자인이 좀 구려보일 수 있지만, 필요한 기능은 보이는 곳에 잘 두었으니 그걸로 만족하기로 했다.

이제 개발 시작해야지

기획도 하고, 디자인도 했으니 이제 개발을 시작해야한다. 그동안 내가 가장 많이 사용해왔던, React를 활용했고 파일 데이터를 다루는 작업이라 우선 Javascript를 활용하기로 했다.

지금은 혼자지만

코드 컨벤션을 설정해주었는데, Eslint와 prettier를 적용해주었다. 혹지 모르잖아? 누군가 같이 하게될지도 ... 기본 적인 부분들 먼저 설정해주고 추후에 추가하기로했다.

// .eslintrc.json
{
  "env": {
    "browser": true,
    "es2021": true,
    "node": true
  },
  "extends": ["eslint:recommended", "plugin:react/recommended"],
  "parserOptions": {
    "ecmaVersion": "latest",
    "sourceType": "module"
  },
  "plugins": ["react"],
  "rules": {
    "no-unused-vars": ["warn"],
    "react/prop-types": ["off"]
  }
}
// .prettierrc
{
  "tabWidth": 2,
  "useTabs": false,
  "semi": true,
  "singleQuote": true
}

그 다음으로 관심사 분리를 위해 폴더 구조를 아래와 같이 나누고 시작했다. 여기를 참고 했다.

여기

열심히 만들어 둔 디자인 시스템도 styled-components를 활용했고, theme.js를 만들어 상수와 GlobalStyle로 지정했다.

export const theme = {
  colors: {
    p100: '#edf2ff',
    // ...,

    white: '#ffffff',
    // ...,

    gray100: '#F1F3F5',
    // ...,
    
    gs100: 'rgba(222, 226, 230, 0.20)',
  },
  fontSize: {
    title700: '2.5rem',
    // ...,
    body700: '2rem',
    // ...,
    button: '2.25rem',
  },
  fontWeight: {
    title700: '700',
   	// ...,
    body700: '700',
 	// ...,
    button: '400',
  },
  lineHeight: {
    title700: '80%',
    // ...,
    body700: '90%',
    // ...,
    button: '150%',
  },
};

Uㅓ ... 파일은 처음 다루는데요 ...

지금까지 웹 사이트를 만들고 서버에서 데이터를 가져와 보여줄 수 있는 경험은 했지만 파일을 다루는 것은 처음이었다. 업로드 하고, drag & drop까지는 순조롭게 개발 할 수 있었지만, 개발 요청 중 파일 안에 있는 데이터를 보이도록 해달라고 하셨기 때문에 조금 더 많이 고민이 필요했다.

우선 흐름을 천천히 파악해보았다.

  1. 파일을 받아들이고
  2. 파일 속의 데이터를 읽어와서
  3. 사용자가 볼 수 있도록 해주자

문제는 어떻게? 보여주느냐의 문제였는데, 나는 이 데이터를 내가 가장 잘 다룰 수 있는 JSON 형태를 이용하기로 했고 함수의 형태로 만들어 재사용 가능하도록 했다.

  1. 데이터를 문자열 형태 인자를 받아온다.
  2. 개행문자를 기준으로 구분하여
  3. 맨 윗 라인(헤더라인)을 추출해 배열에 저장한다.
  4. 그 다음 라인 부터 전제를 객체의 형태로 만들고 하나씩 매칭시켜준다.
  5. 각 내용 행의 객테를 하나의 배열에 담고 반환한다.

이렇게 함수를 돌리고 나면 아래와 같이 사용자에게 보여줄 수 있다. 샘플 데이터이기도 하고 알아보지는 못할테지만, 저렇게 띄어쓰기 된 형태로 잘 나온다는 것만 알면 된다.

날 힘들게 했숴. 파일 다운로드 기능.

저기 위에 화면 보면 알겠지만, 파일이 올라가면 Hex 파일 변환 이라는 버튼을 클릭하고 약 2초간 변환하는 시간을 거친 후 파일 다운로드 버튼이 활성화 된다.

처음에 파일 다운로드 기능을 window.showSaveFilePicker를 사용했는데 사내 서버에 배포하면서 문제가 발생했다.

바로 https만 지원한다는 점... 혹시 사용하게 된다면 꼭 참고하라.
이에 대해서는 팀원 다같이 문제를 공유하면 좋을 것 같아서 노션에 따로 정리했었다.

그래서 기존에는 fileStream과 blob객체를 활용하였었지만, 통하지 않았기에

const handleFileDownload = async () => {
    try {
      const fileHandle = await window.showSaveFilePicker({
        suggestedName: SUGGEST_FILENAME,
      })
      const fileStream = await fileHandle.createWritable()
      await fileStream.write(generateBlob(hexValueArr))
      await fileStream.close()
    } catch (error) {
      alert(ERROR.SAVE)
}

약간 우회하는 방법을 선택했다. FileBtn 컴포넌트 밑에 a태그를 비밀스럽게 하나 만들어주고

<div>
	 <FileBtn
        text="파일 다운로드"
        type="button"
        onClick={handleFileDownload}
        disabled={isChanged}
     />
     <a disabled={isChanged} style={{ display: 'none' }}></a>
</div>

setAttribute를 활용해 objectURL을 만들어준다음 그 링크가 다운되도록 해주었다.

 const handleFileDownload = async () => {
    try {
      let tempLink = document.createElement('a')
      tempLink.setAttribute(
        'href',
        URL.createObjectURL(generateBlob(hexValueArr)),
      )
      tempLink.setAttribute('download', SUGGEST_FILENAME)
      tempLink.click()
      URL.revokeObjectURL(tempLink.href)
    } catch (error) {
      alert(ERROR.SAVE)
    }

아래 참고자료를 첨부했으니 혹 궁금하시다면 확인해보시길..!!

참고자료
https://kwonkyo.tistory.com/599#gsc.tab=0
https://stackoverflow.com/questions/31214677/download-a-reactjs-object-as-a-file
https://code.tutsplus.com/how-to-save-a-file-with-javascript--cms-41105t

그 파일의 데이터가 정확해? CRC 계산 기능.

파일의 데이터를 변환하는 것이 주 목적이다 보니 이 또한 내부에 정해진 규칙들이 있어 까다로웠다. 고정되는 값들도 있고, 고정되는데 데이터에 따라 변환되는 값도 있고, 숫자 변환 방식 다르고, 문자 변환 방식 다르고 등등...

그 중 하나가 CRC 계산을 하는 부분이었는데, 진짜 한 일주일은 붙잡고 매달렸다.
그와중에 쓰레기 값도 나와서 처리해주느라 시간이 더 결렸던 것 같다.

우선 CRC는 데이터를 전송할때 그 전송된 값에 오류(노이즈)가 있는지 확인할 수 있는 체크값이다. 모든 데이터 중에서 고정값 일부분 + 데이터 부분을 CRC 계산기에 돌려 계산된 값을 사이에 넣는것이 목적이다.

타입 혹은 종류가 조금만 달라져도 값이 크게 변하기 때문에 로직을 잘 짜야한다.
검색해보니 c언어로 된 로직은 많이 보였지만, JS로 된 로직은 없어서 직접 작성해주었다.

export function crc16(buffer) {
  let crc = 0xffff;
  let odd;

  for (let i = 0; i < buffer.length; i++) {
    crc = crc ^ buffer[i];

    for (let j = 0; j < 8; j++) {
      odd = crc & 0x0001;
      crc = crc >> 1;
      if (odd) {
        crc = crc ^ 0xa001;
      }
    }
  }

  return crc;
}

그리고 이렇게 계산된 crc16의 값을 계산해주었는데, 자릿수가 길어지니 숫자가 짤리는 현상이 발생했다. 그래서 두 자리씩 끊어서 계산한 다음 배열에 넣어주었다.

import { crc16 } from './crc16'

export default function calculateCRCValue(arrCsvContentHex) {
  const crc16Array = arrCsvContentHex.slice(10)
  const crcValue = crc16(Uint8Array.from(crc16Array))

  const valueHex = parseInt(crcValue, 10)
  if (valueHex > 255) {
    const high = valueHex >> 8
    const low = valueHex & 0xff
    arrCsvContentHex.splice(66, 0, low)
    arrCsvContentHex.splice(67, 0, high)
  }

  return arrCsvContentHex
}

메모장 타입 변환 ...? 그거 어떻게 하는건데...

아주 험난한 과정을 거쳐 데이터 변환을 했는데, 중간 회의 과정에서 알았다. 우리가 알고있던 메모장의 타입과는 조금 다르다는 것을. 처음 기획때 부터 변환하는 확장자 자체가 다르긴했다.(회사에서 자체적으로 만든 확장자...) 근데 파일 타입 까지 다를줄 몰랐지

보통이라면 Window(CSRF), UTF-8 이어야 하지만, 내가 다운 받아야 할 파일은 Macintosh(CR),ANSI형태의 파일이다. 이 부분도 꽤나 고생했는데 파일 타입도 여러가지가 있다는 것을 처음 알았다.

스택오버플로우에서 해결책을 찾았는데, blob객체를 생성하고난 후 Buffter에 담아서 변환해주고, application/octet-stream 타입을 사용하는 것이었다.

import { BLOB_TYPE } from '../constants';

export default function generateBlob(arrCsvContentHex) {
  // arrCsvContentHex : 데이터가 담긴 배열
  let textArea = arrCsvContentHex;
  let arrayBuffer = new ArrayBuffer(textArea.length);
  let unitArray = new Uint8Array(arrayBuffer);

  for (let i = 0; i < textArea.length; i++) {
    unitArray[i] = textArea[i];
  }
  
  // BLOB_TYPE = 'application/octet-stream';
  let blob = new Blob([unitArray], { type: BLOB_TYPE });

  return blob;
}

BLOB_TYPE이라는 상수로 지정을 해주고 기존에 text/plain에서 바꾸어주었다.

이렇게 바꾸어주니 내가 원하는 형태로 파일이 다운받아지는 것을 확인할 수 있었다.

끝났다고 끝난게 아니다.

우여곡절 끝에 개발을 완료하고 서버 환경 구축까지 해준다음 프로젝트를 올리는데 성공했다. 파트장님이 주신 개발 서버 접속 정보를 활용해서 Node, npm, yarn을 설치하고 git에서 프로젝트를 받아왔다.

이렇게 개발이 끝났지만, 나에게 한가지 관문이 남아있었다. 바로, 사용자 테스트. 개발 팀 내에서 필요한 웹 프로그램이었기에 이들이 직접 사용할 수 있어야한다.

파트장님, 선임님께서 여러 테스트 파일을 넣어보시고 issue를 등록해주셨다.

참고로 보안과 관련된 부분은 가렸다. 내가 생각치도 못한 부분에서 버그를 발견하시는걸 보면서 사용자의 입장과 개발자의 입장이 확연하게 차이가 나는 것을 확인했다.

마무리 & 소감

사실 혼자 기획부터 배포까지 한 경험이 완전 처음은 아니다. 친구가 자퇴를 결심하는 바람에 학교에서 이미 한 번 경험했다. 그때에 비하면 지금이 훨씬 아주 훨씬 나은것 같다. 왜냐면 그때는 프론트도 하고, 서버도 만들고, 도커까지 다뤘어야했는데... 지금은 서버를 다루는 비중이 확 줄어들었다. 그저 프론트 프로젝트를 사내 서버에 올리기만 하면 되고, 파일의 데이터만 잘 다루면 할 수 있으니까.(참고로 이 이야기도 나중에 프로젝트를 조금 다듬고 나서 작성할 예정이다.)

그래도 나 혼자 한다는건 외로운일이다. 누군가와 코드에 대해 이야기하고, 더 나은 코드를 작성하고 싶고, 다른 사람들과 내 코드 보면서 토론하기도하고, 논쟁도 해보고 싶은데 그럴 수 없다는게 너무 아쉽다... 그래서 스터디도 하고, 모임 같은 것도 자주 참여하기도한다. 그럼에도 불구하고 채워지지 않는게 있다. 회사 코드고, 회사 사람이 더 잘 코드를 아니까. 같이 이야기 하기도 비교적 편하고 좋을 것 같다는 생각을 한다. 물론 이 과정에서 배우는 많은 것들은 나에게 유익한 경험이 되었지만 웹을 주로 하는 파트가 아니기 때문에 코드에 관해 함께 의견을 나눌 사람이 없다. 나는 아직 프론트엔드 적으로 부족한 부분이 있고, 이를 회사에서 사람들과 함께 성장하는걸 기대했는데 개인적으로 아쉬운 부분이다.

아무튼. 이제 실습 나온지 2달이 넘어가는데 프로젝트 하나를 완수 할 수 있어 다행이다. 2주 후면 종료되는데 고민이 많다. 여러 사람들과 이야기도 해보고, 나 스스로도 고민해보는 시간을 가져봤는데 정확한 해결책은 잘 모르겠다. 하지만 한 가지 확실한건 뭐가되었든 내가 어떻게 하고 싶은지, 뭘 원하는지 확실히 아는 것이다. 분명 나랑 비슷한 고민하는 친구들도 있을거라고 생각하는데 잘 고민해봤으면 좋겠다. 다들 화이팅 🍀

profile
프론트엔드를 ㅅrㄹ6ㅎH
post-custom-banner

4개의 댓글

comment-user-thumbnail
2023년 12월 21일

고등학생이라는게 정말 믿기지 않을 정도로 일을 정말 잘 하시내요. 글도 잘 쓰시구요.
어디가서든 성공하실 거로 보입니다.

1개의 답글
comment-user-thumbnail
2023년 12월 27일

벨로그 상단에 떠서 홀린듯 클릭했는데 미진이 글이었엉..ㅋㅋㅋㅋㅋ 🥺
진짜 끈기랑 문제해결능력 넘 멋있다..! 👍 매번 볼 때마다 엄청 빠르게 성장하는 것 같아..!
항상 응원합니닷!!! 🔥

1개의 답글