-
Notifications
You must be signed in to change notification settings - Fork 50
[1팀 도희정] Chapter2-1. 프레임워크 없이 SPA 만들기 #53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
JunilHwang
left a comment
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
이 피드백은 n8n + ai (gpt-5-mini)를 활용하여 자동으로 생성된 내용입니다.
전체적으로 잘 구성된 Signal/라우터 기반 구조입니다. home.js를 중심으로 상태를 Signals로 분리하고 SearchBox, ProductList, TotalProductCount 등의 컴포넌트를 조합해 SPA를 구성하는 방향성이 잘 잡혀 있습니다. 다만 아래와 같은 확장성 지점이 눈에 띕니다.
- 검색/카운트는 클라이언트 누적 데이터가 아니라 서버의 전체 결과를 기반으로 만들어야 공유 가능한 URL·정확한 카운트를 제공할 수 있습니다. 현재 구조는
getAllProducts로 필터링해search를 구현하는데, 결과가 로드된 페이지 레벨로 국한되어 있는 점을 보완해야 합니다. - 페이지 진입 및 역방향 이동에서
current파라미터를 높게 설정한 URL을 처리하기 위해loadInitialData가 1~N 페이지를 순차적으로 가져오는 구조는 확장성이 떨어집니다. 공유 URL을 빠르게 로드하려면 해당 페이지만 가져오고, 필요하다면 이전 페이지는 추가로 요청하는 방식으로 설계하는 편이 좋습니다. - 라우팅 간 상태 관리/사이드 이펙트에서는
ProductDetail,RelativeProductList,IntersectionObserver등에서 모듈 전역 객체를 사용하거나 네트워크 요청을 취소하지 않아 여러 번 이동할 경우 이전 상태가 뒤엉키는 위험이 있습니다.
이 외에도 updateURL의 히스토리 푸시 전략, 필터 이벤트 바인딩 방식, 콘솔 로그 등에서 개선 여지가 있습니다. 위 이슈들을 단계적으로 해결하면 지금 구조의 재사용성과 유지보수성이 훨씬 올라갈 것입니다.
질문에대한 답변
상태관리 / Signal
createSignal은 말씀하신 것처럼 상태 변경을 구독자에게 통지하는 관찰자(observer) 패턴의 일종입니다. 매번 set()을 호출하면 내부에서 값이 바뀌었는지 확인한 후, 등록된 콜백들을 호출하므로 구독 기반 상태 전파가 가능합니다. 다만 순수한 Observer와 달리 Signal은 getter/setter/subscribe를 한 쌍으로 묶어서 get()으로 읽고, set()으로 쓸 수 있다는 점이 간편합니다. 결론적으로 말씀하신 구조가 바로 Observer 패턴이며, React의 state나 Svelte의 store와도 원리는 같은 흐름입니다.
검색 관련
현재 검색은 클라이언트에 setAllProducts로 쌓인 페이징 데이터를 필터링하는 방식입니다. page 2 이상의 상품을 찾을 때 1페이지를 먼저 내려야만 결과를 얻는 이유가 바로 여기에 있기 때문에, 서비스가 ‘검색어만으로 전체 데이터셋을 조회할 수 있어야 한다’는 요구를 가지면 서버 측에서 search 파라미터로 직접 필터링해서 내려주는 방식으로 전환해야 합니다. 그러면 무한 스크롤을 사용하는 동안에도 검색어 하나로 전체 결과를 보여줄 수 있습니다. 즉, applyFilters에서 클라이언트 필터 대신 fetchProducts({ search: ..., page: filters.current, ... })를 호출하는 식으로 옮겨가야 합니다.
상품 전체 개수
표시하고 싶은 것은 API가 계산한 totalCount이지 클라이언트에 쌓은 배열의 길이가 아닙니다. getProducts()에는 limit=20이 들어있는데, 이것을 없애는 방식은 실질적인 해결책이 아닙니다. API가 응답할 때 totalCount를 넣고, setTotalCount처럼 Signal을 만들어 TotalProductCount(getTotalCount())을 렌더링하면 전체 카운트를 정확히 보여줄 수 있습니다. 클라이언트가 받아온 건수는 ‘지금까지 로드한 아이템 수’로 별도 표기하면 됩니다.
SPA 내비게이션 / SPA 방식 테스트
현재는 history.pushState와 renderPage를 통해 링크를 가로채고 있기 때문에 브라우저가 실제로 페이지 리로드를 하지 않습니다. 이 조건을 확인하려면 개발자 도구의 Network 탭에서 링크 클릭 시 document 또는 document/html에 대한 200 응답이 없는지, Mixed Content 로그 없이 router.js가 렌더링 루틴만 다시 실행되는지 확인하면 됩니다. 또한 SPA 방식으로 페이지 간 이동이 부드럽게 처리된다는 테스트는 pushState가 호출되는지, window.location.reload() 호출이 없는지, 그리고 popstate 이벤트로 뒤로·앞으로 조작이 반영되는지를 함께 점검하면 됩니다. 브라우저의 ‘뒤로 가기’ 버튼을 눌러도 전체 페이지가 새로고침되지 않고 DOM만 재구성되는지 확인해보세요.
| async function loadProducts(page = 1) { | ||
| try { | ||
| if (!getHasMore() && page > 1) { | ||
| console.log("No more products to load"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문제 상황
현재 applyFilters()는 getAllProducts()로 누적된 페이지 데이터를 클라이언트에서 필터링해서 검색 결과를 보여줍니다. 이 구조에서는 아직 로드하지 않은 페이지에 있는 상품은 검색 대상으로 아예 포함되지 않으므로, 예를 들어 URL에 current=3이 설정된 상태에서 page 5에만 존재하는 상품을 찾고 싶으면 이전 페이지를 다 로드할 때까지 아무 결과도 나오지 않습니다.
추가 요구사항 예시
서비스 요구사항이 “검색어 기준으로 전체 상품을 빠르게 찾아주기(페이징과 무관하게)”로 바뀌면, 지금처럼 클라이언트의 누적 목록만 서치하는 방식은 답이 될 수 없습니다. 실제 API에서
search파라미터를 받아서 원하는 결과만 내려줘야 합니다.
근본 원인
- 현재 검색은 이미 가져온 페이지를 대상으로만 수행되므로 아직 로드하지 않은 상품은 제외된다.
loadProducts()에서 API에search파라미터가 전달되지 않으므로 서버에서는 검색 결과를 계산하지 않는다.
개선 구조
// ❌ 현재 방식
function applyFilters() {
const allProducts = getAllProducts();
const filteredProducts = allProducts.filter(...);
setProducts(filteredProducts);
}
// ✅ 개선된 방식
async function applyFilters() {
const filters = getFiltersFromURL();
const productList = await fetchProducts({
...filters,
search: filters.search,
limit: filters.limit,
page: filters.current,
});
setAllProducts(productList.products);
setProducts(productList.products);
}결과
- 검색은 전체 데이터셋을 대상으로 하고, 페이지를 직접 입력해도 누락 없이 결과를 보여준다.
- 기록용 URL도 서버 검색 조건과 일치하게 유지되어 공유/복원이 가능해진다.
| } | ||
|
|
||
| // 페이지 초기화 후 상품 목록 로드 | ||
| function resetAndLoadProducts() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문제 상황
TotalProductCount를 렌더링할 때 products.length를 쓰고 있어서 화면에 나오는 숫자는 ‘현재까지 클라이언트가 받은 상품 수’입니다. 무한 스크롤으로 페이지 3까지 내려가면 60개처럼 증가하지만, API가 실제로 갖고 있는 전체 카운트(category1, search 등 조건을 포함한 전체 개수)와는 다를 수 있습니다.
추가 요구사항 예시
마케팅 팀에서
현재 필터 조건에 해당하는 전체 상품 수를 화면 상단에 표시해 달라는 요청이 들어왔을 때, 지금처럼 클라이언트에 쌓인 길이만을 쓰면 ‘더보기’ 여부나 리뷰 메시지를 정확히 전달할 수 없습니다.
개선 구조
- API 응답에
totalCount필드를 추가/활용하고,loadProducts()에서 이 값을 Signal로 만들어 두어TotalProductCount(totalCount)를 렌더링한다. - 클라이언트
setProducts처리는 기존대로 하되, 개수 표시만 서버 총량을 사용.
코드 비교
// ❌ 현재 방식
setProducts(filteredProducts);
TotalProductCount(products.length);
// ✅ 개선된 방식
setProducts(productList.products);
setTotalCount(productList.totalCount);
TotalProductCount(getTotalCount());결과
- 숫자가 실제 API 조건의 총합과 일치하므로 사용자 인식이 정확해지고, ‘총 1,234개 중 일부’처럼 메시지도 만들 수 있습니다.
| // sentinel 요소 관찰 시작 | ||
| const sentinel = document.querySelector("#infinite-scroll-sentinel"); | ||
| if (sentinel) { | ||
| observer.observe(sentinel); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문제 상황
loadInitialData()에서 URL 쿼리의 current를 읽고 해당 페이지까지 for 루프로 순차적으로 loadProducts(page)를 호출하고 있습니다. 대형 데이터셋에서 브레이크포인트가 current=50이라면 1~49페이지를 차례로 불러와야 하므로 페이지 진입 성능이 급격히 떨어집니다.
추가 요구사항 예시
고객이 검색 URL을 공유해서
/product_list?current=30으로 방문하면 29번의 네트워크 요청과 연쇄적인 렌더링이 발생해야 하고, 이런 구조에서는 초기 화면이 나오기 전에 수초가 걸리며 비용도 비례해서 증가합니다.
개선 구조
loadProducts(page)가 단일 페이지를 요청할 수 있도록 해서, 처음 진입할 때targetPage만 요청한다.- 필요하다면 API에서
page와limit조합으로 총합을 알려줘야 하며, 이전 페이지의 결과는 클라이언트가 필요로 하지 않으므로 로컬 캐싱(예:setAllProducts)도page별로 관리.
코드 비교
// ❌ 현재 방식
for (let page = 1; page <= targetPage; page++) {
await loadProducts(page);
}
// ✅ 개선된 방식
await loadProducts(targetPage);결과
- deep link나
current값이 큰 경우에도 첫 번째 응답만 받아 곧바로 화면을 그리게 되어 UX가 개선됩니다.
| const currentFilters = getFiltersFromURL(); | ||
| updateURL({ | ||
| ...currentFilters, | ||
| current: page, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문제 상황
loadProducts()가 네트워크 요청을 날릴 때 이전 요청을 취소하거나 식별하는 로직이 없습니다. 사용자가 필터를 빠르게 여러 번 바꾸면 오래된 응답이 마지막에 도착해서 setProducts/setAllProducts를 덮어쓰게 되고, 화면에는 뒤집힌 순서의 데이터가 들어올 수 있습니다.
추가 요구사항 예시
필터를 연속으로 누르고 ‘검색’ 버튼을 누르는 동작을 하면 몇 초 뒤 오래된 결과로 롤백되기 때문에, 사용자에게는 일관되지 않은 결과가 보입니다.
개선 구조
loadProducts()는 내부에서AbortController혹은requestId를 사용해 이전 요청을 취소하거나, 응답에requestId를 붙여서 응답이 마지막으로 보낸 요청인지 확인한 뒤에만 state를 업데이트합니다.
코드 비교
// ❌ 현재 방식
const productList = await fetchProducts(params);
setProducts(...);
// ✅ 개선된 방식
const id = ++latestRequestId;
const productList = await fetchProducts(params);
if (id !== latestRequestId) return;
setProducts(...);결과
- 사용자 입력이 빠를 때에도 화면 상태가 튼튼하게 유지되고, 네트워크 응답 순서에 따라 잘못된 데이터를 덮어쓰지 않습니다.
| @@ -0,0 +1,69 @@ | |||
| import { getProduct as fetchProduct } from "../../api/productApi"; | |||
| import { LoadingInDetail } from "../../components/Loading-in-detail"; | |||
| import { Breadcrumb } from "./components/Breadcrumb"; | |||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문제 상황
productDetail.js에서 isLoading/productData를 모듈 최상단에서 선언해서 모든 ProductDetail 호출이 공유하고 있습니다. 빠르게 상품을 이동할 경우 이전 상품의 데이터가 다음 렌더에 이어지거나, 첫 번째 응답이 아직 오는 도중에 두 번째 응답이 덮어쓰면서 renderProductDetail()에서 상태가 뒤섞입니다.
추가 요구사항 예시
제품 상세를 두 번 연속 빠르게 누르거나
history.back()을 짧은 시간 안에 하면, 클릭한 상품이 아닌 다른 상품의 정보가 잠깐 보이거나 에러가 나는 현상이 심해질 수 있습니다.
개선 구조
ProductDetail을 호출할 때마다 내부 상태를 캡슐화한 객체를 만들어 사용하고, 진행 중인 요청도 해당 호출에 귀속되게 한다.- 예를 들어
const controller = new AbortController()를 만들어서loadInitialData가 끝날 때까지 보호하고, 새로운ProductDetail요청 때 이전 controller를 abort.
코드 비교
// ❌ 현재 방식 (module scope)
let isLoading = true;
let productData = {};
// ✅ 개선된 방식 (per call)
export const ProductDetail = (() => {
return (params) => {
let localLoading = true;
let localProduct = {};
// ...
};
})();결과
- 각 상품 페이지가 독립된 상태를 갖게 되어 빠른 라우팅/프리페칭에도 문제가 없고, 기억해야 할 부수 효과를 줄일 수 있습니다.
| container.innerHTML = renderRelativeProducts(); | ||
| } | ||
| } catch (error) { | ||
| console.error("Failed to load relative products:", error); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문제 상황
관련 상품을 가져오는 로직에서 setTimeout(() => loadRelativeProducts(...), 0)을 돌리고 있고, API 호출이 끝난 이후 현재 DOM이 여전히 존재하는지 확인하지 않습니다. 사용자가 바로 다른 상품 상세로 이동하면 이전 요청이 끝나면서 container.innerHTML을 조작하려고 하고, 이미 DOM이 제거되어 있는 상황에서 에러가 나거나 잘못된 박스를 다시 그리게 됩니다.
추가 요구사항 예시
SPA에서 상품 상세 간 이동이 빨라졌을 때, 이전 요청이 남은 상태에서 DOM을 조작하면 console 에러가 발생하거나 오래된 관련 상품이 섞여 나오는 문제가 생깁니다.
개선 구조
loadRelativeProducts가 실행될 때 현재 렌더된productId를 캡슐화하고, 응답이 도착할 때if (productId !== currentProductId) return;같은 가드를 둔다.- 또는
AbortController를 사용해서 이전 관련 상품 요청을 취소하고, DOM이 없는 경우를 검증한다.
결과
- 상세 페이지 간 이동이 빨라도 DOM이 없는 상태에서 조작하지 않기 때문에 에러를 피하고, 관련 상품 리스트가 현재 상품과 항상 일치합니다.
| }); | ||
| console.log("Sort select event attached"); | ||
| } else { | ||
| console.warn("sort-select element not found!"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문제 상황
SearchBox에서 이벤트 연결을 setTimeout(() => { ... attachFilterEvents(); })로 처리하고 있습니다. 렌더링 직후에 DOM을 innerHTML로 교체하고 나면, 다음 틱에서 이벤트를 바인딩하도록 되어 있는데, 이 사이에 다른 렌더링이 또 발생하면 attachFilterEvents가 오래된 DOM을 참조하거나 스킵되는 일이 생깁니다.
추가 요구사항 예시
필터/정렬을 빠르게 여러 번 바꾸면
limit-select나sort-select가 잠시 동작하지 않다가 몇 밀리초 후에 다시 살아나는 듯한 현상이 있다면, 현재 수동setTimeout방식이 타이밍에 민감하다는 뜻입니다.
개선 구조
SearchBox를 렌더링한 직후에attachFilterEvents()를 호출하도록 바꾸고, DOM insertion 후 콜백을 실행하려면requestAnimationFrame이나Promise.resolve().then()같은 방법을 사용.setTimeout대신renderUI()의innerHTML직후에 명시적으로attach...를 호출하거나 도움 함수에 DOM을 넘겨 리턴받은 노드에 붙인다.
결과
- 이벤트 바인딩이 렌더링과 항상 동기화되어 클릭/변경 시점에 바로 동작하며, 타이밍에 따른 버그를 줄일 수 있습니다.
| setupPopStateListener(); | ||
| loadInitialData(); | ||
| setupIntersectionObserver(); | ||
| }, 0); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문제 상황
subscribeProducts에서 로그 후 setupIntersectionObserver()를 setTimeout으로 재설정하고 있지만, **Home 컴포넌트가 언마운트될 때(예: 상품 상세로 이동)**에는 observer.disconnect()가 호출되지 않아 이전 observer가 DOM 없이 살아있는 상태가 됩니다. 이 상태에서 다시 홈에 돌아오면 새로운 observer를 만들기 이전에 오래된 observer가 여전히 살아 있어서 메모리 누수 및 불필요한 작업을 수행합니다.
추가 요구사항 예시
홈 → 상세 → 홈을 여러 번 반복하면
IntersectionObserver가 매번 물리적으로 생성되지만, 이전 인스턴스가 해제되지 않아서 성능이 떨어지고 디버깅하기 어렵습니다.
개선 구조
renderPage()가 홈을 렌더링할 때setupIntersectionObserver()를 호출하는 대신 홈을 렌더링하는 시점에observer.disconnect()를 먼저 실행하도록 한다.- 또는
Home에서return () => observer?.disconnect();같은 언마운트 훅을 직접 구현하여 라우터가 다른 페이지로 이동하기 전에 명시적으로 해제한다.
결과
IntersectionObserver가 항상 하나만 유지되며, 홈 외의 페이지에서는 아무 작업도 하지 않아서 메모리 & CPU를 절약할 수 있습니다.
| filters.limit, | ||
| filters.sort, | ||
| filters.category1, | ||
| filters.category2, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문제 상황
updateURL()이 필터 변경마다 history.pushState({}, "", newURL)를 호출하기 때문에 필터를 드롭다운에서 여러 번 바꾸면 브라우저 히스토리는 동일한 페이지가 수십 개 쌓이게 됩니다. 사용자가 뒤로가기를 눌렀을 때 필터 값이 여러 단계로 되돌아가는 경험은 좋지 않고, 원하지 않는 URL이 남습니다.
추가 요구사항 예시
필터 UI가 SPA 특성상 즉시 반영되고, 뒤로가기는 ‘실제 페이지 이동’이 되었을 때만 동작하길 기대한다면, 현재 설계대로는 ‘정렬을 4번 클릭 → 뒤로’ 하면 아예 필터 히스토리끼리 왔다 갔다 하고 결과적으로 홈으로 가기가 어렵습니다.
개선 구조
- 필터 옵션을 바꿀 때는
history.replaceState()를 사용해서 현재 엔트리만 업데이트하고, 명시적으로 “카테고리 이동” 같은 내비게이션에만pushState()를 쓰도록 나눈다. - 또는
pushState()를 쓰더라도 필터 값이 실제로 변경된 경우에만 호출하고,current/limit등이 같으면 skip한다.
결과
- 사용자 히스토리에는 실제 ‘페이지 전환’만 기록되고, 뒤로 가기/앞으로 가기가 직관적으로 작동합니다.
|
|
||
| // current는 기본값(1)이 아닌 경우에만 추가 | ||
| if (filters.current && filters.current > 1) { | ||
| params.set("current", filters.current.toString()); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
문제 상황
loadProducts, loadInitialData, setupIntersectionObserver, attachFilterEvents 등 곳곳에 console.log/console.warn가 남아 있어서 생산 환경에서도 로그가 계속 찍힙니다. 이러면 퍼포먼스가 떨어질 뿐 아니라, 운영 중에 수만 건의 로그가 쌓여 디버깅 히스토리를 어지럽힙니다.
추가 요구사항 예시
실제 배포 환경에서
console.log가 너무 많으면 F12 콘솔이 도저히 못 쓰게 되고, 또 로그 수집 시스템에 불필요한 잡음이 쌓여서 진짜 에러가 묻힐 수 있습니다.
개선 구조
- 개발용 로깅은
DEV플래그(import.meta.env.DEV) 아래에 두거나,log()헬퍼로 감싼 후 prod 에선 무시하도록 한다. - 필요 없는 로그는 제거하여 기능 중심으로 코드를 읽기 쉽게 유지한다.
결과
- 배포 후 콘솔이 깔끔해지고, 실제 문제를 추적하기 쉬워집니다.

과제 체크포인트
배포 링크
https://dev-learning1.github.io/hanghae-plus_front_7th_chapter2-1/
기본과제
상품목록
상품 목록 로딩
상품 목록 조회
한 페이지에 보여질 상품 수 선택
상품 정렬 기능
무한 스크롤 페이지네이션
상품을 장바구니에 담기
상품 검색
카테고리 선택
카테고리 네비게이션
현재 상품 수 표시
장바구니
장바구니 모달
장바구니 수량 조절
장바구니 삭제
장바구니 선택 삭제
장바구니 전체 선택
장바구니 비우기
상품 상세
상품 클릭시 상세 페이지 이동
/product/{productId}형태로 변경된다상품 상세 페이지 기능
상품 상세 - 장바구니 담기
관련 상품 기능
상품 상세 페이지 내 네비게이션
사용자 피드백 시스템
토스트 메시지
심화과제
SPA 네비게이션 및 URL 관리
페이지 이동
상품 목록 - URL 쿼리 반영
상품 목록 - 새로고침 시 상태 유지
장바구니 - 새로고침 시 데이터 유지
상품 상세 - URL에 ID 반영
/product/{productId})상품 상세 - 새로고침시 유지
404 페이지
AI로 한 번 더 구현하기
과제 셀프회고
처음에는 UI 템플릿이 주어졌으니 최대한 구글검색으로 개발하려고 했습니다. 하지만 속도가 너무 더디고 기한 내에 개발할 수 있는 양이 너무 적을 것 같아, AI 도움을 통해 학습하면서 개발해야 겠다고 판단했습니다. AI의 도움을 받아 작업을 진행했지만, 원리를 이해하고 제가 이해할 수 있는 코드로 수정하기 까지 시간이 많이 소요되었습니다. 그러다 보니 생각보다 작업한 양이 많지 않아 저에게 실망(?)하게 되었습니다.
Vanilla JS로 처음 개발해보았는데, 이번 과제를 통해 자주 사용해 오던 기술들의 근본적인 원리에 대해 다시 한번 생각해 볼 수 있는 계기가 되었습니다.
기술적 성장
자랑하고 싶은 코드
어떤 것이 잘된 코드인지 감이 안와요...
코드 자랑은 아니지만...(테스트 리스트에는 없었지만)검색에 해당하는 데이터가 없을 때의 VIEW를 추가했습니다.
개선이 필요하다고 생각하는 코드
검색에 따른 데이터 필터
학습 효과 분석
JS를 사용한 경험이 많이 없어서, 이것에 대해 다시 리마인드할 수 있어서 좋았습니다. 자주 사용하고 있는 기술들의 원리를 깨닫고, 더 나아가 스스로 개발할 수 있도록 고민하는 부분이 필요한 것 같습니다. 이러한 고민들이 결국 어떤 기술을 사용하든 웹 내의 동작 매커니즘 안에서 동일하게 사용되고 응용되는 것이라고 생각하기 때문입니다.
과제 피드백
과제의 각 요구사항들을 영상으로 제공해 주어서 좋았습니다.
AI 활용 경험 공유하기
한 페이지 내에서 간단한 기능을 구현할 때는 ChatPT를 사용했습니다.
Cursor는 간단한 기능을 구현해달라고 해도 생각보다 복잡하게 개발하는 경향이 있는 것 같습니다.
여러 파일들의 연관이 강할 때는 아무래도 Cursor를 사용해서 개발을 한 후, 수정된 내용에 관해 이야기를 하여 좀 더 간략화 및 가독성 좋게 수정해 나갑니다.(수정된 내용을 보고 제가 수동으로 다시 수정하여 이에 맞게 다시 수정해 달라고 하는 경우도 있습니다.)
리뷰 받고 싶은 내용
상태관리 (결국 ai 돌려서 작업했습니다.)
[상태에 변화가 있을 때, 구독자에게 알림] 이와 같이 동작을 한다고 하는데 이것이 결국 observer 방식 아닌가요?
검색
상품 전체 개수
상품 상세 페이지 내 네비게이션
→ 원하는 조건에 맞게 잘 실행이 되고 있는 것인지 어떻게 확인할 수 있나요?
페이지 이동
→ 원하는 조건에 맞게 잘 실행이 되고 있는 것인지 어떻게 확인할 수 있나요?