본문 바로가기
Programming/Sabujak Sabujak

사이드 프로젝트 톺아보기[1] - 나만의 만다라트 만들기

by mingule 2021. 9. 18.

들어가기

오랜만에 예전에 했던 사이드 프로젝트들을 살펴보기로 했다. 

리액트로 만든 나의 첫 사이드 프로젝트! 만다라트부터 이야기를 해보려고 한다. 

tmi가 정말 많을 예정이다. 그냥 tmi 그자체 . 케케케 

 

대학시절 친구의 추천으로 알게 된 만다라트 기법! 

하나의 큰 목표를 가지고 그를 실행하기 위한 목표를 9x9 Table에 차근차근 세워나가는 기법이다.

일본의 야구선수 오타니 쇼헤이가 작성했다고 해 한 때 인기를 끌었다고 한다.

 나도 한 때 열심히 만들긴 했었지 ^^;;; 

당시에 반짝 재밌게 목표를 세웠었지만 세월이 지남에 따라 기억 속에서 잊혀져 있었는데,

우아한 테크코스를 하면서 한 코치분께서 만다라트를 소개해주시면서 다시 생각이 났다. 추억,,,,, (포코 감사해요 푸힝)

웹 개발자라는 새로운 목표를 향해 나아가기 위해 다시 한 번 만다라트를 직접 그려 한땀 한땀 채워나가던 찰나에

이걸 굳이 손으로 그리지 않고 조금 더 쉽게 만들 수 있도록 템플릿을 제공해보면 어떨까? 라는 생각이 들었다.

딱 React에 첫 발을 떼던 시점이었어서, 배운 것을 조금 활용하면서 복습도 하자는 느낌으로? 프로젝트를 시작했다. 

Input이 엄청 많았어서.. 고생을 많이 했는데 고생을 한 만큼 재미도 있었다. 크크크 

별 건 없지만 내가 어떻게 만들었는지 돌아보면서 (코드를) 회고해보려고 한다. 푸헤헤. 재밌겠다.

그럼 꼬우꼬우~

 

🪜 프로젝트 환경설정하기

프로젝트 환경 설정은.. React를 사용할 것이었기에 Create React App(CRA)을 사용했다.

환경 설정을 하나씩 해볼까 라는 욕심이 잠깐 고개를 들기도 했지만..!

세부 사항을 하나하나 설정해나가면서 프로젝트를 만들기에는 너무 프로젝트 시작부터 느려질 것 같았다.

당시 웹팩에 대해서 (조금) 이해하고 있기는 했으나 아직 학습 단계였기에(진짜 이유) 빠른 환경 설정이 가능한 CRA를 택했다. 

그래서.. 아래 코드로 환경설정 완료우!

npx create-react-app mandal-art

Style 적용은 당시 새로 배웠던 CSS-in-JS 라이브러리인 Styled-Components를 적용했다. 

JS 코드를 CSS에서 사용할 수 있다니! 신기하기도 했고, Style 관련된 코드와 JS 코드가 분리되어 한결 깔끔해져서 좋았다.

또 CSS in JS 형식은 해당 컴포넌트가 렌더링 될 때에만 컴포넌트 단위의 Style 태그를 생성해내기 때문에

모듈화도 신경 쓸 필요가 없었다.(최근에 SCSS 모듈화에 실패해서 이제와서 돌아보니 모듈화 자동으로 해주는거 왜이렇게 좋아보이냐 ㅠㅠ)

 

여담으로 지금 나는 CSS in CSS 방식인 SCSS를 자주 사용하고 있는데 두 방식을 모두 사용해 본 결과,

CSS in JS 방식이 Style에서 JS 코드를 사용할 수 있다는 큰 장점과 모듈화를 자동으로 해줘서 편하다는 것을 느낀 것 이외에는 크게 막 CSS in JS가 쩐다..! 라고 느끼거나(사실 저게 쩌는 포인트이긴 한데..) 막 아 겁나 별로다.. 라고 느꼈던 점은 없었다.

그니까 딱 그냥 아 ! 좀 편하구나 ! 정도..?! ㅋㅋㅋㅋㅋㅋㅋㅋ

 

흠 그런데 최근에 성능 미션을 하면서 새로운 점을 알게 됐다.

CSS in JS를 사용하면 일단 라이브러리를 사용하는 것이기에 번들 크기도 커지고, 

JS의 CSS 코드를 읽어와 파싱하는 단계부터 시작하기 때문에 CSS in CSS보다는 성능을 저하시킨다고 하더라!

우리 팀은 CSS in CSS를 사용해서 잘 몰랐던 점이었는데,

다른 팀원들의 이야기를 듣고 실제로 생각보다 요런 성능 저하 문제가 유의미하게 있을 수 있구나- 를 깨달았다. 

상황에 따라 적절히 CSS in JS나 CSS in CSS를 사용하면 좋을 것 같다고 생각했다. 

 

그리고 개인적으로는 CSS in CSS가 조금 더 좋긴 하다. 뭔가 퓨어한 느낌..! ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ

그리고 내가 CSS에서 JS 사용 못해서 조금 덜 편하더라도

내 프로젝트를 사용하는 유저들이 조금이라도 더 편할 수 있다면..! 77ㅑ륵 ㅎㅎㅎㅎ

 

아, 여담으로 요즘 Tailwind같은 CSS Framework도 핫하던데 편하긴 해보인다!

그래도 나는 내가 CSS 직접 짜는게 좋아..! 짜릿해..!

 

아래는 CSS in CSS vs CSS in JS 성능을 비교한 글이다. 재밌어서 가져와봤당. (영어다.)

https://pustelto.com/blog/css-vs-css-in-js-perf/

 

Real-world CSS vs. CSS-in-JS performance comparison

I took the real app and convert it from Styled Components to Linaria to compare the app performance of CSS-in-JS and normal CSS. Continue reading if you want to know how it went.

pustelto.com

 

🤔 Component 만들기

그럼 프로젝트 환경 설정을 다 했으니.. 이제 나는 코드만 작성하면 됐다. 호홌

개인적으로 나는 재사용 할 수 있는 컴포넌트를 먼저 짜고, 그 컴포넌트들로 페이지를 쌓아나가는 것을 좋아해서

일단 내가 만들고자 하는 페이지를 대략적으로 머리속에서 구상해나갔다. ㅎㅅㅎ.. ㅎㅎ.ㅎ..

디자인 시안을 먼저 짰어야 했는데 단순한 페이지라서.. 만들면서 디테일을 적용했다.ㅋㅋㅋㅋㅋㅋㅋ 

이건 개인 프로젝트였기에 가능했던 부분이었던 것으로..! 

포스팅에는 전체 컴포넌트를 돌아보지는 않을 것이고, 중심이 되는 컴포넌트들을 살펴보면서 글을 작성해보겠다.

 

아래는 만다라트를 검색하면 가장 많이 나오는 오타니 쇼헤이의 만다라트 예시다. 요 예시를 보면서 페이지를 구상했다. 

이 예시에 대해 잠깐 설명하자면, 아래와 같다.

먼저 중앙 input에 나의 최종 목표를 적고, 그 주위에 최종 목표를 위해 필요한 8가지 키워드를 적는다.

그리고 그 키워드가 하나의 목표가 되어 또 그 주위에 키워드를 위해 해야 할 8가지의 일들을 적으면 된다. 

생각보다 되게 간단한 표이다. (하지만 개발은 그렇지 않았지)

 

일단 처음에는 3x3 Table이 9개 있으니, 3x3 Table을 만들어 재사용하고자 했다.

그렇게 Table이라는 컴포넌트를 하나 만들었다. 

 

이 Table 컴포넌트도 원래 1개 만들어놓고 9개에 다 재사용하려고 했는데

약간의 욕심이 더해져서 나중에 나머지 8개의 Table을 제어하는 가장 중앙의 Main Table 컴포넌트를 하나 더 추가했다. 

 

9x9 Table 중앙에 위치한 3x3 Table을 Main Table로 정하고, 

그 Main Table의 중앙에 있는 최종 목표를 위한 8개의 키워드를 입력하면

둘러싼 8개의 Table의 중앙에 목표가 입력되게 하려고 했기 때문이다. 

(말이 겁나 장황한데, 아래처럼 동작하는 테이블을 만드려고 했다는 거다.ㅋㅋㅋㅋㅋㅋㅋ)  

TMI)

본래는 Table을 Input 태그를 이용해 만들었었는데, 나중에 Textarea로, 그 이후에 contentEditable로 바꿨다. 

이렇게 바꾼 내용에 대한 것을 당시에 포스팅했었기 때문에 여기에는 자세히 적지 않겠다. 

궁금하신 분은 여기로! 헤헹

 

✔️ Table Component

Table 컴포넌트를 한 번 살펴보자. 중복코드가 너무 많아서 몇 줄만 가져와보겠다. ㅋㅋㅋㅋㅋㅋㅋㅋ ㅠㅠㅠ

전체 코드가 궁금하신 분들이 있다면.. 여기에서 보시면 좋을 것 같아요.. 

Table 컴포넌트는 아래와 같이 만들었다. 

<TableWrapper centerInput={centerInput} main={main} className='table-wrapper'>
  <Cover main={main} centerInput={centerInput} />
  <div className='boxes'>
    <div onClick={onClickSpan} className='hide-input first-first'>
      <span
      contentEditable={true}
      onPaste={onPreventStylePaste}
      data-index={0}
      className={`box ${customClass}`}
      onBlur={main ? onChangeMainInput : handleSubTableInputs}
      autoComplete='off'
      data-placeholder={main ? '목표 1' : ''}
      onKeyDown={onPreventShiftTab}
    	/>
  	</div>
    
	...
    
  </div>
</TableWrapper>

 

Styled Components 쓰면서 어디는 적용하고 어디는 안적용하고.. 혼란 그자체

게다가 필요없는 className까지 안지우고 놔뒀다. ㅋㅋㅋㅋㅋㅋㅋ 지워야겠다....

 

일단 그래도 한 번 살펴보자............

TableWrapper가 3x3 Table 전체를 감싼다. 

그리고 boxes라는 div는 한 row를 감싸고 있다. 즉, boxes가 div 3개를 가진다.

Cover는 가장 중앙의 Table을 먼저 작성하기 전에 나머지 Table을 작성하지 못하도록 하는 역할을 한다. 

원래 input자체를 disable하는 정석적인 방법만 생각하느라 고민을 엄청 많이 했었는데, 

생각보다 쉬운 방법으로 해결했다. 케케케!!!

근데 이 방법은 또 그냥 가려지기만 하는거라, shift + tab을 누르면 아래에 있는 input이 focus 되는 신박한 문제가 튀어나왔다.ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 요 짤이 생각났다...

 

그리고 span에 contentEditable 속성을 줘서 input이 아니지만 input처럼 동작할 수 있게 했다. 

이거.. 검나 고생하긴 했지만 재밌는 기능이다. 조심히 써야되는 것 같다. ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

당연하지만 input을 쓰는 게 정석이다...! 무조건!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!

접근성 측면에서도 input이나 textarea를 쓰는 것이 훨-씬 좋다. 

당시 나는 input은 multi-line이 되지 않는다는 점에서 사용하지 않았고, 

textarea의 커서를 정중앙에 넣지 못해서(내가 구현 못해서) 사용하지 않았다.

(Textarea 중앙정렬 가능한데 다시 찾아보고 정리해야겠다 저번에 정리한다고 하고 까먹었다.. )

내가 원하는 방식으로 커서를 정 중앙에 제대로 정렬하지 못하는 건 결국 contentEditable에서도 마찬가지였는데,

그래서 span을 div로 감싸 마치 중앙정렬인 척 했다. ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ ㅜㅜ

그래도 보다보니 나 어떻게든 해내는 사람이었네..?ㅎ ㅎ뿌듯. ; ; ; 

 

 

contentEditable속성은 복사 붙여넣기 할 때 style까지 다 가져와버리기때문에,

이를 막기 위한 onPreventStylePaste 함수를 만들어 붙여줬고, 

아까 잠깐 말했던 shift+tab을 막아주기 위해서 onPreventShiftTab 함수도 만들어 붙여줬다.

중앙에 있는 Main Table을 조작하면 나머지 8개의 Table의 중앙에 있는 Input에 값을 넣어주기 위해서 onBlur 이벤트도 붙였주었다.

지금보니까 저 handleSubTableInput은 아무 역할도 안하는데 왜 넣어준지 모르겠다. 하하하 지워야지..

 

어쨌든.. 뭐 이런 식으로 짰다. 코드가 다 중복이라 사실 Table의 메인 기능은 요거다. 

별 거 없지만 별 거 있는 코드~ 예에~ 

기억 더듬으면서 정리하니까 되게 재밌다. ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ (좀 부끄럽기도 하고)

나중에 리팩토링도 재밌을 것 같다. 기대된다. 지울꺼 많아서 행복하게 리팩토링 할 수 있을 것 같다..

 

✔️ Input Component

일단 이름을 뭐 이따위로 지었지

아마 Table을 제외하고 Input이 이것밖에 없어서 이렇게 지었던 것으로 기억한다 ^^;;;;;;;;;;;;;;;;;;;;; ㅎ

이 Input Component는 요기처럼 만다라트에 이름을 넣는 용도로 사용된다. 

 

입력하기 전 초기 상태는 아래처럼 되어있는데, 나중에 크루들에게 피드백을 받았는데 Input인줄 몰랐다고 한 크루들이 좀 있었다. 

조금 더 Input처럼 보이도록 디자인을 좀 바꿔야겠다.. ㅋㅋㅋㅋㅋㅋㅋㅋㅋ

 

어쨌든, 별 거 없어보이지만 이게 되게 골치아팠다.

왜냐면!!!!!!!!!!!! 사용자들한테 만다라트를 만들고, 이미지로 저장할 수 있게 해주고 싶었다. 

그런데 만다라트 표만 딱 저장하면 밋밋하니까.. 누구의 만다라트인지 제목까지 예쁘게 넣어서 제공해주고 싶었다.

그래서 이렇게 이름을 직접 넣어서 예쁘게 저장하게 만들어주고 싶었는데.......... 문제가 하나 있었다.

이건 multi line이 필요없는 곳이니까, Input을 사용하면 됐다.

근데 만약 그냥 width가 정해진 Input을 넣게되면 가운데 정렬이 안된다......................... 아래처럼..!

이것도 사실 내 욕심이긴 했다. 하하하! input resizing이 재밌어 보이기도 했고, 

내 귀여운 가운데정렬 디자인을 ............. ............... 포기하고 싶지 않았다..................... 

 

리사이징하면 아래처럼 된다. 한글자는 가운데정렬이 잘 안되긴 한다 ^^;; (이것도 고쳐야지)

당시에 영어 + 한글을 같이 쓰는 것도 고려하다보니까 영어는 또 한글보다 크기가 작아서 적당한 선에서 타협을 봐야 했던 부분도 있고,

input width를 더 줄이게되면 글자를 치면서 Input이 늘어나는게 사용자에게 보이게 되어서 요쯤에서 스스로 타협을 봤다. 하하하!

나중에 코드 고치면서 조금 더 예쁘게 만들 수 있을 것 같다. 끙! 

 

리사이징 이벤트는 아래와 같이 붙였다. ㅋㅋㅋㅋㅋ진짜 RAW한 발상이다. 더 코드가 궁금하신 분들은 요기에서 보실 수 있읍니다.

const resize = ({ target }) => {
  target.style.width = `${target.value.length * 0.6 + 1.65}em`;
};
  
<InputWrapper>
  <input
    onKeyUp={resize}
    onFocus={onHandleFocusOn}
    onBlur={onHandleFocusOut}
    onKeyDown={onPreventTab}
    maxLength='6'
    placeholder='구루는구루밍'
  />
</InputWrapper>

length가 늘어나면 그만큼 Input width가 늘어나도록 했다.

저 0.6과 1.65에 무슨 의미가 있냐고 물으신다면 없다고 당당히 대답할 수 있다. 하하하!

그냥 열심히 눈으로 대조해가면서 width가 어떻게 늘어나야 가장 예쁘게 보이는지 확인한 결과다......... 하하하......

이것도 만들면서 마음에 걸리긴 했다. 무조건 리팩토링 각인데..? 하면서 만들었다. 

아직까지 고민을 더 못하긴(안하긴) 해서 요 부분은 더 고민해봐야할 것 같다. 

더 smooth하고 아름답게 구현하고 싶은 욕심이 있다. 푸힝 


뭐 이외에는 크게 살펴볼만한 게 없다.

워낙 기능이 적은지라.. 헤헤 (다행이다) 코드 살펴보는거 재밌긴 했다. ㅋㅋㅋㅋㅋㅋㅋ

 

💥 의도치 않은(?) 리팩토링

사실 이번에 이렇게 포스팅을 하다보니 리팩토링을 하고 싶긴 했다. 쓸데없는 코드가 너무 많이보이고 코드 중복도 심하다.

생각해 둔 아이디어도 있어서 새로운 기능을 만들고 싶기도 했다 .ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ ㅠㅠ

프로젝트를 돌아보는 것이 목적이라 이번에 코드를 크게 리팩토링 하려고 했던 것은 딱히 아니었기도 하고..

또 무엇보다 지금 당장은 바빠서 못하겠지만.. (힝) 우테코 수료하면 하나씩 해나가야겠다는 다짐을 다시금 하게 됐다. 

 

그래도 필요없는 코드를 조금 삭제하거나 deprecated된 메소드들을 바꾸거나 하는 식의 리팩토링은 살짝 했다. 

당시에는 deprecated되지 않았던 것 같은데..(?!) 이번에 deprecated 된거겠지 ^^;;?ㅋㅋㅋㅋㅋㅋㅋ 

(코드를 짤 때에는 MDN을 꼭 살펴보면서 짭시다.)

어떤 프로젝트든 유지보수가 중요하구나.. 를 깨달았다.

이렇게 코드가 deprecated 되어 내가 의도했던대로 프로젝트가 동작하지 않게 되면 안되니까! 하하하!!!

리팩토링도 재밌네.. 코드리뷰 하는 느낌.. 

 

🥳 프로젝트를 돌아보며..

당시에 내가 어떻게 코드를 짰었는지, 무슨 생각을 가지고 코드를 짰는지 돌아보았다. 추억여행 느낌 . .

기억이 나기도 하고, 어떤 코드는 이해가 잘 안가는 코드도 있었다. 이거 내가 짠 거 맞아?!!!!!!!

아마 당시의 나도 공부한답시고 이것저것 막 적용해보면서 마구 적용했던 내용들도 조금 있어서 였을 거 같다.ㅋㅋㅋㅋ

그래도 그때 공부하면서 내가 생각해낸 최선의 코드였겠지?!!!!!! 하하하!!!!! 

 

아 그리고 뭔가 쓰다보니까 이게 .. .. .. 뭘 쓴 건 지 모르겠다.

프로젝트 돌아보기였으니까 돌아보긴 했으니까 된건가..? ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ아놔 .

글쓰기 연습좀 더하자 . 나자신아!!!!! 

 

지금까지 배워온 것들이 정말 많지만, 배우면 배울수록 더 배울것이 많아져서 

괜스레 제자리걸음을 하고 있는 것 같아 마음이 조급해지던 중에, 

프로젝트를 돌아보면서 뭔가 그래도 5개월 전의 나보다 지금의 내가 조금 더 성장하긴 했구나 라는 것을 느낄 수 있어서 좋았다.

이전에는 잘 이해가 가지 않았던 내용들도 이제 돌아보면 조금 더 빠르게, 그리고 쉽게 이해할 수 있다.

내 페이스에 맞추어 조금씩 가다보면 미래에 나는 더 성장해 있겠찌! 음하하! 

화이팅 나자신! >_ㅇ 

 

댓글