과제 체크포인트
배포 링크
https://dev4n4.github.io/front_6th_chapter2-1/
기본과제
- 코드가 Prettier를 통해 일관된 포맷팅이 적용되어 있는가?
- 적절한 줄바꿈과 주석을 사용하여 코드의 논리적 단위를 명확히 구분했는가?
- 변수명과 함수명이 그 역할을 명확히 나타내며, 일관된 네이밍 규칙을 따르는가?
- 매직 넘버와 문자열을 의미 있는 상수로 추출했는가?
- 중복 코드를 제거하고 재사용 가능한 형태로 리팩토링했는가?
- 함수가 단일 책임 원칙을 따르며, 한 가지 작업만 수행하는가?
- 조건문과 반복문이 간결하고 명확한가? 복잡한 조건을 함수로 추출했는가?
- 코드의 배치가 의존성과 실행 흐름에 따라 논리적으로 구성되어 있는가?
- 연관된 코드를 의미 있는 함수나 모듈로 그룹화했는가?
- ES6+ 문법을 활용하여 코드를 더 간결하고 명확하게 작성했는가?
- 전역 상태와 부수 효과(side effects)를 최소화했는가?
- 에러 처리와 예외 상황을 명확히 고려하고 처리했는가?
- 코드 자체가 자기 문서화되어 있어, 주석 없이도 의도를 파악할 수 있는가?
- 비즈니스 로직과 UI 로직이 적절히 분리되어 있는가?
- 코드의 각 부분이 테스트 가능하도록 구조화되어 있는가?
- 성능 개선을 위해 불필요한 연산이나 렌더링을 제거했는가?
- 새로운 기능 추가나 변경이 기존 코드에 미치는 영향을 최소화했는가?
- 코드 리뷰를 통해 다른 개발자들의 피드백을 반영하고 개선했는가?
- (핵심!) 리팩토링 시 기존 기능을 그대로 유지하면서 점진적으로 개선했는가?
심화과제
- 변경한 구조와 코드가 기존의 코드보다 가독성이 높고 이해하기 쉬운가?
- 변경한 구조와 코드가 기존의 코드보다 기능을 수정하거나 확장하기에 용이한가?
- 변경한 구조와 코드가 기존의 코드보다 테스트를 하기에 더 용이한가?
- 변경한 구조와 코드가 기존의 모든 기능은 그대로 유지했는가?
- (핵심!) 변경한 구조와 코드를 새로운 한번에 새로만들지 않고 점진적으로 개선했는가?
과제 셀프회고
전반적인 회고
수제(직접만들다) 리팩토링에 도전한 나 (악악!!!!!😰)
과제를 시작하면서 더티코드를 처음 봤을 때
- 왠지 혼자서도 수정할 수 있을 것 같다는 이상한 자신감이 들었고 (SI의 더티코드 사이에서 한번 굴러본 나는 가능할지도?! 라는 근자감이었죠..!)
- AI를 쓰면 넘나 확확 바꿔줄 것 같아서 더 정신없을 것 같았고
- 혼자 더티코드를 수제로 바꿔보는게 더 얻고 느끼는게 많지 않을까?
라는 생각에 AI 없이 수제로 코드를 리팩토링 해보기 시작했다!
초반에는 내가 다른 분들보다 더 빠르게 작업을 하고 있는 것 같다는 생각이 들었다. 다들 AI가 너무 코드를 확확 바꿔줘서 여러번 갈아 엎었다고 하시는데 나는 갈아엎지 않고 꾸준히 진행하고 있으니까 뭔가 잘 진행하고 있는 것 같았고 별의별 생각이 다 들었다.
대체로
🤔 오… 사실 AI를 사용하는 게 함정 아니었을까! 😳 AI 주도 개발은 독이 든 성배였던 것인가! 😙 AI를 사용한 개발자보다 AI를 사용하지 않은 개발자가 더 속도가 빠르다는 게 이런 것인가!
같은 생각이 들었었는데 결국 시간 배분에 실패하고 아 역시 AI를 적당히 사용하면서 과제를 진행했어야했나..! 하고 후회가 되었다.
과정 회고
일단 처음에는 prettier와 ESLint를 세팅했다.
그리고 세팅해보면서 prettier 문서도 한번 훑어봤다. prettierignore가 있다는 것을 이번에 처음 알았다. 신기해서 프로젝트에 적용시켜봤다. 재미있었다.
그 다음에는 일단 눈에 보이는 것 부터 고쳐보자는 생각에 var를 let, const로 변경했다. 그리고 UI 로직과 상수값을 분리하고자 하였다.
그런데 처음에 한번에 분리하는 데에는 실패하고… 결국 적당히 눈에 보이는 데 까지만 분리한 다음 남은 UI 로직들은 다른 걸 진행하면서 점진적으로 분리해봐야겠다 생각하고 넘어갔다.
그 다음으로는 흩어진 전역변수를 상단으로 집합시킨 뒤, 전역변수들을 하나씩 따라가면서
- 전역변수여야만 하는 것
- 지역변수여도 괜찮은 것
으로 분류해 가면서 전역변수의 수를 줄여나갔다.
여기까지 진행했을 때도 오 처음보다는 많이 나아졌구나 하는 생각이 들었다. 그 다음엔 의미없는 익명함수들을 삭제하고 function 문을 화살표 함수로 변경했다. 문자열끼리 더해준 부분도 백틱을 사용하는 것으로 코드를 개선했다. 그리고 함수를 하나씩 보면서 코드를 깔끔하게 개선해 나갔다.
이때 변수명을 좀 더 가독성 좋게 바꿔보려고 하였고, 미처 못바꿔준 변수명들은 리팩토링이 다 끝나고 나서 흐름을 읽으며 변수명을 바꿔줘야겠구나~ 하고 생각했다.
배열을 순회하는 for 문을 forEach, map, filter 등으로 변경해주었다. for 문 들을 개선하면서 한 for 문 안에서 여러가지 일이 동시에 벌어지고 있는 구문을 보면서 이걸 분리해야하나 말아야하나 고민을 좀 했었다.
“이걸 분리하면 for 문 하나로 해결 될 일이 for 문 3개가 될 텐데 그렇다면 성능에 안좋은 영향을 끼치게 되는 것이 아닌가?”
같은 생각이 들어서 고민하다가 석호님께 물어봤고 시간복잡도 라는 개념에 대해 처음으로 알게 되었다.
그래서 시간복잡도란?
- 시간복잡도(빅오 표기법) 기본 개념
시간복잡도는 입력 크기 nnn이 커졌을 때 알고리즘이 얼마나 많은 연산을 수행하는지를 대략적으로 나타내는 척도이다. 대표적으로 사용하는 표기법은 **빅오(Big O)**로, 가장 성장률이 빠른 항만 남겨서 표현한다.
예시:
- O(1) : 입력 크기와 무관하게 일정한 시간이다. (예: 변수 하나에 접근)
- O(n) : 입력 크기에 비례하여 시간이 늘어난다. (예: n번 반복)
- O(n²) : 입력 크기의 제곱만큼 시간이 늘어난다. (예: 중첩 루프)
중요한 점은 “한 번 도는 for문 1개”와 “세 번 도는 for문 3개”는 둘 다 O(n)이라는 점이다.
즉, 입력 크기 nnn에 대한 성장률 관점에서는 동일하다. 다만 실제 수행 시간은 상수배 차이가 있다.
예를 들면:
// A: 한 루프 안에서 세 가지 작업
for (let i = 0; i < arr.length; i++) {
doA(arr[i]);
doB(arr[i]);
doC(arr[i]);
}
// B: 루프를 세 개로 분리
for (let i = 0; i < arr.length; i++) {
doA(arr[i]);
}
for (let i = 0; i < arr.length; i++) {
doB(arr[i]);
}
for (let i = 0; i < arr.length; i++) {
doC(arr[i]);
}
두 경우 모두 **O(n)**이지만, B는 A보다 루프 오버헤드와 반복 횟수가 3배이므로 실제 연산량은 더 많다.
2. 상수항과 실제 성능: 빅오는 대략, 현실은 디테일
빅오 표기법은 nnn이 매우 커졌을 때의 성장률만 보는 것이며, 실제 실행 시간에는 다음과 같은 추가 요소들이 영향을 준다.
1). 상수 항 (constant factors)
예시에서 `doA`, `doB`, `doC` 각각이 비용이 작다면 루프를 분리해도 체감 성능 차이가 미미할 수 있다. 반대로 각각이 무거운 작업이라면 세 개로 분리한 구조가 눈에 띄게 느릴 수 있다.
2). 캐시 지역성 (cache locality)
하나의 루프 안에서 `arr[i]`를 한 번만 읽고 여러 작업을 하면, CPU 캐시에 올라온 데이터를 재사용할 수 있어서 메모리 접근 비용이 줄어든다. 반면 루프를 분리하면 배열을 여러 번 순회하게 되어 캐시 미스가 늘어날 수 있다.
3). 분기 예측 / 파이프라인
복잡한 조건문이 섞인 한 루프보다 책임이 분리된 깔끔한 루프가 CPU 파이프라인에서 더 예측 가능하게 작동할 수 있다. 다만 상황에 따라 반대일 수도 있다.
4). 병렬화 가능성
각각의 루프가 독립적이라면(`doA`, `doB`, `doC`가 서로 영향을 주지 않는다면) 이후에 웹 워커나 다른 병렬 처리 방식으로 분리하여 수행하기가 용이하다.
3. “분리할까, 합칠까” 실무적 판단 기준
다음 기준을 활용하면 균형 잡힌 선택이 가능하다.
1. 합치는 것이 바람직한 경우
- `doA`, `doB`, `doC`가 모두 같은 원소 `arr[i]`에 대해 수행되며, 데이터 접근 비용이 크고 캐시 재사용 이득이 중요한 경우.
- 루프 오버헤드나 메모리 접근이 병목일 때.
- 각 작업이 가볍고 반복이 많아서 상수배 절감이 의미 있을 때.
2. 분리하는 것이 바람직한 경우
- 각 작업이 개념적으로 완전히 독립적이며, 한 루프 안에 섞으면 가독성이나 유지보수성이 떨어질 때.
- 특정 작업만 자주 바뀌거나 개별적으로 테스트할 필요가 있을 때.
- 추후에 일부만 비동기 처리하거나 병렬화할 가능성이 있을 때.
- 한 루프에 데이터 검증, 변환, 통계 집계, 로그 기록 등을 모두 넣으면 복잡도가 올라가고 버그 발생 위험이 커질 때. 이 경우 실제 성능 손해가 크지 않다면 단일 책임 원칙을 따라 분리하는 편이 유지보수에 더 이롭다.
각 작업이 개념적으로 완전히 독립적이며, 한 루프 안에 섞으면 가독성이나 유지보수성이 떨어질 때 분리하는 것이 바람직하다.
arr.length가 수백만 이상인 경우가 아닌 이상에야 가독성을 위해 분리하는 것이 여러모로 유익하다고 한다.
그리고 석호님께서 “원래 성능과 가독성은 반비례한다” 라는 말을 하시면서 조언을 해주셨다
한 파일에 모든 로직을 몰아넣으면 코드 길이가 짧아지고 경우에 따라 성능 이점이 있을 수 있습니다. 그러나 리팩토링의 핵심 목적은 가독성과 유지보수성을 높이는 데 있습니다. 그 결과로 파일 수가 늘어나고, for문을 분리해 반복이 늘어나는 것처럼 보일 수 있지만, 이는 복잡도를 낮추고 향후 수정과 확장을 쉽게 하기 위한 구조적 선택입니다. 현재는 성능에 대한 집착보다, 읽기 쉽고 관리하기 쉬운 코드 작성에 집중해 주세요.
따라서 해당 조언을 받아 for문을 여러개로 쪼개며 리팩토링 하고 더 가독성이 좋아졌음을 체감했으며 “성능과 가독성은 반비례한다” 라는 말에 대해서도 생각하고 느끼게 되었다.
그렇게 함수들을 직접 고쳐보면서 코드를 깔끔하게 만드는 속도와 감각이 늘었음 또한 체감했다. 노가다를 하며 실전근육이 길러진 느낌을 받았다. 어딘가 쓸데가 있겠지 생각했다.
그렇게 즐거운 함.꾸(함수 꾸미기) 를 하다가 테오의 멘토링을 받았다. 테오님께 내 코드를 짧게나마 보여드리고 피드백을 받았는데 정말 충격적이었다.
내 코드는 아주 자연스럽게 DOM에서 Data와 상태를 추출해서 가공해 사용하고 있었다. 즉 데이터가 역방향으로 흐르고 있었던 것이다… 리액트에선 있을 수 없는 일이다. 테오가 리팩토링 할 때 리액트로 변경해도 크게 달라지지 않을만한 형태로 하도록 신경써달라고 했는데.. 😱
그날 천지가 개벽했다.
급하게 상태관리의 필요성을 느끼고 도입해야하나 생각했다. 첫 주차에 했던 작업들이 생각났는데(vanila JS SPA…) 시간상 그렇게까지 도입하는건 무리일거 같다는 생각도 들었다.. 그래서 일단 급한대로 랜더링 로직을 분리하고 factory 패턴을 도입하여 Data를 수정하는 함수가 끝나면 랜더링 로직이 실행되는 형태로 구조를 변경하였다.
음.. 일단은 간단하게나마 데이터가 수정된 이후에 랜더링이 돌아가도록 만드는 데 성공했구나 싶었다. 나는 클린코드… 라고 하면 그냥 예쁘고 깔끔한 코드와 함수만 신경쓰면 되겠거니 했는데 정말 상상도 못했던 부분이었다.
그래서 계획이 틀어져 시간이 부족할 것 같아서 함.꾸를 하다 말고 심화과제로 들어갔다. 리액트로 작업을 하다 보면 기본과제에서 개선점이 보일거라 하셔서 그걸 기대한 것도 있다…
UI 로직을 재활용하고 붙여서 화면을 똑같이 그리는 것 까지는 수월했다. 오!! 나 기본과제 잘한 것 같은데? 라고 생각했다. 이때까지는.. 기본과제에서 vanila JS 특유의 문법들이 데이터 핸들링 함수 내부 곳곳에 여전히 남아있었고… 그것들이 나를 힘들게 하였다. 아… 이걸 리액트로 어떻게 바꾸지? 계속 생각하면서 머리를 싸맸다. 기본과제 코드만 예쁘게 꾸민다고 다가 아니었구나. 생각했다. 이때 미리미리 AI를 적극 활용했어야 했나 하고 제일 많이 후회했다.
머리를 박박 싸매면서 어찌어찌 리액트, TS로 버그 없이 돌아가는 페이지를 만들었지만 지금 내 코드가 예쁜가? 하면… 나의 최선을 다하긴 한거같은데 과연 예쁜 코드일까 싶다…
과제를 하면서 내가 제일 신경 쓴 부분은 무엇인가요?
수제코드! 를 만들어보자!! 한땀한땀 직접 해보면서 몸으로 느껴보자!
를 제일 신경써서 작업했던 것 같다. 좋은 판단이었는지는 모르겠다…
근데 확실히 과제를 하면서 늘었다고 생각된 부분들은 있다.
- 이제 어떤 더러운 코드를 봐도 놀라지 않을 것 같다. 침착하게 분해해서 나름의 절차대로 리팩토링 할 수 있을 것 같다.
- 데이터의 흐름성에 대해 생각하면서 개발을 하게 될 것 같다.
- 하나의 함수는 하나의 일만 한다는 것을 염두에 두고 잘 해부할 수 있다. 함수 해부에 대한 나름의 기준이 생긴듯 하다.
- 함.꾸 속도가 전에 비해 완전 빨라졌다. 적당한 안목과 실전 압축 근육이 생겼다.
다소 미련한 도전이었을지도 모르겠으나 나름의 보람과 수확은 있지 않았나 하는 생각이 든다. 원래 직접 맞으면서 배우는게 더 인상깊으니까 지금 리스트 해본 것 보다 나도 모르게 나에게 남은게 많이 있지 않을까 하는 생각도 든다.
과제를 다시 해보면 더 잘 할 수 있었겠다 아쉬운 점이 있다면 무엇인가요?
- 시간 계산을 더 철저히 해서 진행해 볼 것 같다.
- AI를 좀 더 적극적으로 활용해 볼 것 같다.
기본과제에 비해 리액트로 리팩토링하는 것에 시간투자를 많이 못한 것 같아서 아쉽다.
그리고 AI를 더 잘 활용했다면 지금보다 결과가 더 나았을까? 미련이 남는다. 근데 이건 다른 수강생들의 레포를 보면서 인사이트를 얻을 수 있을 것 같다.
과제가 끝나고 다른 레포들을 많이 구경 다닐 계획이다.. 나보다 어떤 점이 나은지, AI로 인해 어떤 실질적인 도움을 얻었는지, AI를 어떻게 하면 더 잘 활용할 수 있었는지에 대한 것이 궁금하다.
리뷰 받고 싶은 내용이나 궁금한 것에 대한 질문 편하게 남겨주세요 :)
테오가 생각했을 때 “AI를 잘 활용해달라” 라는 건 어떤 방식에서의 활용을 기대하시고 한 조언이었나요?
제가 생각했을 때
- 많이 질문하고 많은 피드백 받기
- 다양한 AI를 사용해보며 물어보기
- 바이브 코딩 체험하기?
- 좋은 프롬포트를 만들어서 먹이고 성과가 나오는 것을 보기
이정도가 좋은 AI 활용이 아니었을까 생각이 드는데 혹시 위에 열거한 점 말고도 “이렇게 활용하는 것이 좋겠다” 라고 생각하신 부분이 있으셨을까요? 궁금합니다.
과제 피드백
수고하셨습니다! ㅋ
"수제(직접만들다) 리팩토링에 도전한 나 (악악!!!!!😰) " ㅋㅋㅋㅋ
실제로 AI에게 모든 것을 맡기면 생산성이 그렇게 까지 높아지지 않는다는 조사 보고서가 나왔죠. 결국 AI가 만든 것들 중에서 좋은 것 좋지 않은 것을 구분하고 판단하고 재지시하고 하는 일들도 다 시간을 써야 하는 일이니까요.
"...나는 갈아엎지 않고 꾸준히 진행하고 있으니까 뭔가 잘 진행하고 있는 것 같았고 별의별 생각이 다 들었다." 라는 건 중요한 인사이트라고 생각해요. 사실 AI가 나오기전에 리팩토링 과제에서 제일 중요하게 강조했던것은 테스트 코드가 없다고 생각하고 내가 수정한 코드가 절대적으로 기존의 로직을 변경하지 않을거라는 확신을 가진채로 조금씩 수정하는 기술이 중요하다고 했거든요
모든 학문들이 그러하듯 원칙은 변하지 않고 그 원칙 위해서 새로운 개념이나 패러다임이 나오는 만큼 정석대로 해보는건 중요하죠. 수고했습니다.
"...테오님께 내 코드를 짧게나마 보여드리고 피드백을 받았는데 정말 충격적이었다. 내 코드는 아주 자연스럽게 DOM에서 Data와 상태를 추출해서 가공해 사용하고 있었다."
라는 걸 알게 된 것도 AI를 쓰지 않고 본인이 해봤기 때문에 시행착오를 통해서 알게 되었고 그게 이론을 공부하는게 아니라 실습이라는 과제를 통해서 체득으로 얻게 되는 지혜라고 생각해요. 왜 나쁜 방법은 나쁜 결과를 가져오는지를 해보지 않고서는 '그런가 보다'하고 넘어가지 알 길이 없을테니까요.
"근데 확실히 과제를 하면서 늘었다고 생각된 부분들은 있다. ...다소 미련한 도전이었을지도 모르겠으나 나름의 보람과 수확은 있지 않았나 하는 생각이 든다." 미련한 거 아니에요. 한번도 가지 않은 길은 한번은 가야 합니다. 잘했어요!
Q) 테오가 생각했을 때 “AI를 잘 활용해달라” 라는 건 어떤 방식에서의 활용을 기대하시고 한 조언이었나요? 제가 생각했을 때 1 많이 질문하고 많은 피드백 받기 2 다양한 AI를 사용해보며 물어보기 3 바이브 코딩 체험하기? 4 좋은 프롬포트를 만들어서 먹이고 성과가 나오는 것을 보기
산들이 말해준 모든 내용들이 있지만 AI를 잘 활용한다라는건 활용법 그 자체라기 보다는 AI를 잘 쓰기 위해서는 결국 내가 원하고자 하는 것을 잘 설명할 수 있을 정도의 지식과 구체적인 표현력이 뒷받침되어야 한다는 것을 느꼈으면 좋겠다고 생각했어요. 내가 모르는데 AI를 구슬려서 해내게 하는 능력도 (생각해보니 중요하네요) 중요하겠지만 내가 알고 있고 원하는 방향대로 만들도록 시켜내기 위해서 결국 기본을 잘해야 하는 구나로 귀결되길 바랬어요.
산들의 수제타코야끼를 굽는 과정을 보니 잘 한것 같네요! 수고 많았습니다. 5주차도 화이팅입니다