Skip to content
Merged
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
155e729
feat: BottomSheet 기본 UI 구현
jstar000 Nov 22, 2025
f66346b
feat: BottomSheet에 React Portal 적용
jstar000 Nov 22, 2025
f921d8a
feat: BottomSheet 애니메이션 적용
jstar000 Nov 22, 2025
83ab5f2
feat: BottomSheet 뒷배경 스크롤 차단 로직 구현
jstar000 Nov 22, 2025
ee13c9d
feat: BottomSheet 드래그 커스텀 훅 구현
jstar000 Nov 22, 2025
b62637e
fix: 터치 이벤트를 sheet에서 content로 이동
jstar000 Nov 22, 2025
a312aa6
fix: sheetDragStartY 초기화 코드 추가
jstar000 Nov 22, 2025
30492d3
feat: DragHandler 컴포넌트 구현, BottomSheet에 적용
jstar000 Nov 22, 2025
9bfce67
refactor: drag 커스텀 훅 메서드 이름 변경
jstar000 Nov 22, 2025
6e6fe58
feat: 드래그 훅 공통로직 함수 분리
jstar000 Nov 22, 2025
827551d
refactor: 폴더 구조 정리
jstar000 Nov 22, 2025
7a6781e
feat: BottomSheet 애니메이션 적용
jstar000 Nov 22, 2025
33705c7
feat: 매직넘버 상수 추출
jstar000 Nov 22, 2025
b3cff8a
chore: 주석 수정
jstar000 Nov 22, 2025
cf6b7ad
fix: sheet 너비 단위 변경(% -> rem)
jstar000 Nov 24, 2025
beca89a
Update src/shared/components/bottom-sheet/hooks/use-bottom-sheet-drag.ts
jstar000 Nov 24, 2025
a08a17c
Merge branch 'feat/bottom-sheet/#40' of https://github.com/SOPT-all/3…
jstar000 Nov 24, 2025
d4c4e11
refactor: 드래그 핸들러 변수명 변경
jstar000 Nov 24, 2025
dcf386b
refactor: 드래그 핸들러 훅 변수 useRef로 수정
jstar000 Nov 24, 2025
b17484f
Merge branch 'develop' into feat/bottom-sheet/#40
jstar000 Nov 25, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 2 additions & 49 deletions src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,54 +1,7 @@
import * as styles from "./test.css";
import {
ArrowLeft,
ArrowRight,
Banner,
Benefit,
BottomSheetHeart,
BottomSheetHeartFill,
ChatFloating,
Check,
ChevronDown,
ChevronRightRounded,
ChevronRightSharp,
ChevronUp,
Delete,
Home,
Logo,
MakerHeart,
MakerHeartFill,
Search,
Share,
ShoppingCart,
Star,
} from "./assets/svg";
import BottomSheetTest from "@/shared/components/bottom-sheet/test/bottom-sheet-test";

function App() {
return (
<div className={styles.test}>
<MakerHeart width={24} height={24} />
<MakerHeartFill width={24} height={24} />
<BottomSheetHeart width={24} height={24} />
<BottomSheetHeartFill width={50} height={50} />
<ArrowLeft width={24} height={24} />
<ArrowRight width={24} height={24} />
<ChevronDown width={24} height={24} />
<ChevronRightRounded width={24} height={24} />
<ChevronRightSharp width={24} height={24} />
<ChevronUp width={24} height={24} />
<Banner width={200} height={200} />
<Benefit width={24} height={24} />
<ChatFloating width={24} height={24} />
<Check width={24} height={24} />
<Delete width={24} height={24} />
<Home width={24} height={24} />
<Logo width={24} height={24} />
<Search width={24} height={24} />
<Share width={24} height={24} />
<ShoppingCart width={24} height={24} />
<Star width={24} height={24} />
</div>
);
return <BottomSheetTest />;
}

export default App;
59 changes: 59 additions & 0 deletions src/shared/components/bottom-sheet/bottom-sheet.css.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
import { style, keyframes } from "@vanilla-extract/css";
import { color } from "@/shared/styles/tokens/color.css";

// overlay 애니메이션 정의
const fadeIn = keyframes({
from: { opacity: 0 },
to: { opacity: 1 },
});

// sheet 애니메이션 정의
const slideUp = keyframes({
from: { transform: "translateY(100%)" },
to: { transform: "translateY(0)" },
});

// overlay: 전체 바텀 시트의 레이아웃 컨테이너
// backdrop과 sheet의 부모 컨테이너, flex-end로 sheet를 화면 하단에 배치
export const overlay = style({
position: "fixed",
top: 0,
left: 0,
right: 0,
bottom: 0,
zIndex: 1000, // TODO: zIndex 토큰화
display: "flex",
alignItems: "flex-end", // bottom sheet는 화면 아래에서 올라와야 하므로, overlay의 bottom에 위치시켜야 함
justifyContent: "center",
animation: `${fadeIn} 0.2s ease-out`,
});

// 반투명 검은 배경, 사용자 클릭 시 바텀시트 닫는 용도
export const backdrop = style({
position: "absolute",
top: 0,
left: 0,
right: 0,
bottom: 0,
backgroundColor: "rgba(0, 0, 0, 0.35)",
});

// 실제 바텀시트(흰색 박스))
export const sheet = style({
position: "relative", // overlay 기준으로 상대적인 위치 설정
width: "100%",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

sheet이 overlay를 기준으로 너비가 100으로 설정 되어서 아래와 같이 pc로 접속했을 때는 전체 화면을 차지하는 것 같아요!

image
  1. overlay는 화면 전체 너비를 차지하되 바텀 시트의 너비는 375px로 고정
  2. 두 너비 모두 375px로 고정

두 가지 선택지가 있을 텐데 저는 후자로 통일감을 주는 게 낫다고 생각하는데 어떻게 생각하시나요??

(혹시 이거 의도하신 거라면 이유를 설명해 주시면 될 것 같습니다 ㅎㅎㅎ)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

저희가 모바일 웹앱이라 컴포넌트를 구현할 때 웹 환경까지 고려해서 구현하지는 않아서 width: 100%로 설정해뒀습니다..!
overlay는 backdrop과 sheet 자식 컴포넌트를 담는 용도로만 사용하고, 크기는 자식 컴포넌트들에서 조절하면 좋을 것 같아 sheet의 width를 375px로 변경했어요!

maxHeight: "90dvh", //
backgroundColor: color.white[100],
borderTopLeftRadius: "16px",
borderTopRightRadius: "16px",
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

px out

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

border-radius는 의도적으로 px로 적용해뒀어요!

rem을 사용하는 목적은 사용자가 브라우저의 기본 글꼴 크기를 조정할 때, rem 단위를 사용하는 요소들도 함께 비례해서 조정하는 것이에요. 이를 통해 웹 accessibility를 향상시킬 수 있어요.

반면 px은 절댓값으로 브라우저 환경과 관계없이 일정한 값을 유지해요. 일반적으로 컴포넌트의 테두리 굵기(border), 곡률(borderRadius) 등 사용자의 글꼴 크기 설정과 관계없이 일정하게 유지되는게 시각적으로 분명하고 디자인 의도를 살릴 수 있는 속성은 px로 값을 설정해요.
px로 값을 사용하는 다른 속성들로는 box-shadow, position(요소를 화면의 고정된 위치에 배치해야 할 때 top: 20px같이 사용) 등이 있어요!

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

와 죄송합니다 단순 오타인 줄 알고 px out 이렇게 남겼는데 이런 뜻이 있었군요 이 무지함에 야유를......;;;; 그럼에도 친절한 설명 감사합니다 ㅜㅜ!!!!!! 완전히 이해 되었습니다

display: "flex",
flexDirection: "column",
zIndex: 1001, // TODO: zIndex 토큰화
animation: `${slideUp} 0.3s ease-out`,
});

export const content = style({
padding: "0 1.6rem 1.6rem 1.6rem",
overflowY: "auto",
flex: 1, // sheet가 column이므로 content가 sheet의 모든 공간을 차지하도록 설정
});
62 changes: 62 additions & 0 deletions src/shared/components/bottom-sheet/bottom-sheet.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import React, { useEffect } from "react";
import { createPortal } from "react-dom";
import * as styles from "./bottom-sheet.css";
import { useBottomSheetDrag } from "./hooks/use-bottom-sheet-drag";
import DragHandler from "@/shared/components/drag-handler/drag-handler";

interface Props {
isOpen: boolean;
onClose: () => void;
children: React.ReactNode;
}

const BottomSheet = ({ isOpen, onClose, children }: Props) => {
const {
sheetRef,
contentRef,
handleTouchStart,
handleTouchMove,
handleTouchEnd,
handleDragHandlerMove,
} = useBottomSheetDrag({ onClose });

// 바텀시트가 열렸을 때 뒷배경 스크롤 방지
useEffect(() => {
if (isOpen) {
const originalOverflow = document.body.style.overflow;
document.body.style.overflow = "hidden";

return () => {
// isOpen이 변경되거나 컴포넌트가 언마운트될 때 실행, body의 overflow를 원래 값으로 복구
document.body.style.overflow = originalOverflow;
};
}
}, [isOpen]);

if (!isOpen) return null;

return createPortal(
// TODO: 접근성 고려
<div className={styles.overlay}>
<div className={styles.backdrop} onClick={onClose} />
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

시간 여유가 있다면!! overlay/backdrop 클릭 시 바텀시트가 바로 사라지지 않고 부드럽게 아래로 내려가는 애니메이션 후 닫히도록 개선하면 UX가 더 자연스러울 것 같아요!

현재는 클릭 즉시 unmount되는데, isClosing 같은 상태를 추가해서 애니메이션 완료 후 onClose()가 호출되도록 하면 드래그로 닫을 때와 일관된 모션을 제공할 수 있을 것 같습니닷.

<div ref={sheetRef} className={styles.sheet}>
<DragHandler
onTouchStart={handleTouchStart}
onTouchMove={handleDragHandlerMove}
onTouchEnd={handleTouchEnd}
/>
<div
ref={contentRef}
className={styles.content}
onTouchStart={handleTouchStart}
onTouchMove={handleTouchMove}
onTouchEnd={handleTouchEnd}>
{children}
</div>
</div>
</div>,
document.body
);
};

export default BottomSheet;
104 changes: 104 additions & 0 deletions src/shared/components/bottom-sheet/hooks/use-bottom-sheet-drag.ts
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

병합 충돌 방지하기 위해 임시로 bottom-sheet 폴더 아래에 bottom-sheet용 hook 폴더를 위치시키신 걸까요??

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

바텀시트에서만 사용되는 훅이라 바텀시트 구현 코드와 함께 묶이는게 좋을 거라고 생각해 bottom-sheet/hooks처럼 선언해두긴 했는데, 처음에 컨벤션으로 정한 것과 일치하는 폴더구조는 아니죠..? 폴더구조도 고민이 많이 되네요...ㅠㅠ

Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import React, { useRef, useState } from "react";

interface UseBottomSheetDragProps {
onClose: () => void;
}

// 상수 정의
const CLOSE_THRESHOLD = 200; // sheet을 닫기 위한 최소 드래그 거리 (px)
const CLOSE_TRANSITION_DURATION = 200; // sheet 닫힐 때 transition 시간 (ms)
const RETURN_TRANSITION_DURATION = 300; // sheet 원위치 복귀 시 transition 시간 (ms)

export const useBottomSheetDrag = ({ onClose }: UseBottomSheetDragProps) => {
const sheetRef = useRef<HTMLDivElement>(null);
const contentRef = useRef<HTMLDivElement>(null);
const [startY, setStartY] = useState(0); // 드래그를 시작한 y좌표
const [sheetDragStartY, setSheetDragStartY] = useState<number | null>(null); // sheet을 내리기 시작한 y좌표
const [dragDistance, setDragDistance] = useState(0); // sheet이 내려간 거리
const [isDragging, setIsDragging] = useState(false);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

요 두 상태는 상태로 관리할 필요가 없어보입니다.
(필히 수정해 주세요... 는 아니지만 시간 여유가 있다면 리팩토링 해보는 게 어떨지 제안 드립니다... ㅎ.ㅎ)

  • dragDistance는 지역 변수로 관리해도 충분해 보여요

지금 코드에서 드래그할 때마다 moveSheet가 실행되고 moveSheet 안에서 sheetDeltaY를 계산하는데 그걸 dragDistance에 저장하죠!
즉, dragDistance는 그냥 sheetDeltaY를 복사해서 보관하는 형태가 되죠

또, dragDistance가 활용되는 유일한 곳이

if (dragDistance > CLOSE_THRESHOLD) {
  onClose();
}

이 부분 뿐이라 마지막 드래그 거리만 알 수 있으면 충분해 보여요!!! 즉, 드래그 과정 전체를 state로 추적할 필요 없이 지역 변수로 처리해도 동작에는 문제가 없고, 오히려 불필요한 리렌더도 줄일 수 있을 것 같습니닷

  • isDragging는 ref로 관리하는 게 더 적절해 보여요

이 값은 단순히 "지금 드래그 중인지"를 판단하기 위한 논리 플래그로만 쓰이는데 이런 값은 state로 둘 경우 리렌더를 불필요하게 유발할 수 있고,

또 state는 비동기 업데이트라 드래그 시작 직후 값이 즉시 반영되지 않을 수도 있는데, ref로 관리하면 즉시 반영되고 렌더에도 관여하지 않아서 더 안정적일 것 같습니다

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

dragDistance는 드래그가 종료될 때 실행되는 메서드인 handleTouchEnd에서만 사용되는데 useState로 선언해서 드래그 할 때마다 리렌더가 발생하도록 구현할 필요가 없죠 지당하십니다
렌더링에 사용되는 값도 아니므로 dragDistanceref로 구현하면 좋을 것 같아요(let 지역변수로 선언하는 것보다 더 안정적, 예측 가능)
isDragging도 마찬가지 이유로 ref로 선언하는게 더 적합하다는 의견에 동의합니다ㅎㅎ

세심한 부분까지 신경쓰시면서 리뷰 남겨주셔서 감사합니다~


// 드래그 시작
const handleTouchStart = (e: React.TouchEvent) => {
setStartY(e.touches[0].clientY); // 드래그를 시작한 y좌표 세팅
setIsDragging(true);
};

// sheet을 드래그하는 로직
const moveSheet = (currentY: number) => {
if (sheetDragStartY === null) {
setSheetDragStartY(currentY);
}

const sheetDeltaY = currentY - (sheetDragStartY ?? currentY);
setDragDistance(sheetDeltaY);

if (sheetRef.current) {
sheetRef.current.style.transition = "none"; // handleTouchEnd에서 추가된 transition 제거 -> sheet가 드래그에 즉각 반응
sheetRef.current.style.transform = `translateY(${sheetDeltaY}px)`;
}
};

// content 영역 드래그
const handleTouchMove = (e: React.TouchEvent) => {
if (!isDragging) return;

const currentY = e.touches[0].clientY; // 현재 드래그 y좌표
const deltaY = currentY - startY; // 드래그 이동 거리

// 드래그가 위에서 아래로 진행될 때 if문 진입(content를 위로 스크롤하거나, 바텀시트를 닫기 위한 드래그 액션)
if (deltaY > 0) {
// content의 최상단에 도달했을때 if문 진입 및 sheet 드래그
if (contentRef.current && contentRef.current.scrollTop === 0) {
moveSheet(currentY);
}
}
};

// drag handler 영역 드래그
const handleDragHandlerMove = (e: React.TouchEvent) => {
if (!isDragging) return;

const currentY = e.touches[0].clientY;
const deltaY = currentY - startY;

if (deltaY > 0) {
// drag handler 조작 시 scrollTop 체크 없이 바로 sheet 드래그
moveSheet(currentY);
}
};

// 드래그 종료
const handleTouchEnd = () => {
setIsDragging(false);

if (sheetRef.current) {
// CLOSE_THRESHOLD 이상 드래그하면 닫기
if (dragDistance > CLOSE_THRESHOLD) {
// 부드럽게 아래로 내려가며 닫기
sheetRef.current.style.transition = `transform ${CLOSE_TRANSITION_DURATION}ms ease-in`;
sheetRef.current.style.transform = "translateY(100%)";

// transition 완료 후 onClose 호출
setTimeout(() => {
onClose();
}, CLOSE_TRANSITION_DURATION);
} else {
// sheet 원위치로 복귀
sheetRef.current.style.transition = `transform ${RETURN_TRANSITION_DURATION}ms ease-out`;
sheetRef.current.style.transform = "translateY(0)";
}
}

setDragDistance(0);
setSheetDragStartY(null);
};

return {
sheetRef,
contentRef,
handleTouchStart,
handleTouchMove,
handleTouchEnd,
handleDragHandlerMove,
};
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
// 바텀시트 테스트용
// 추후 삭제 예정

import { style } from "@vanilla-extract/css";
import { typographyVars } from "@/shared/styles/typography.css";
import { color } from "@/shared/styles/tokens/color.css";

export const container = style({
padding: "2rem",
minHeight: "100vh",
});

export const title = style({
...typographyVars.heading1,
marginBottom: "2rem",
color: color.black[200],
});

export const openButton = style({
padding: "1.2rem 2.4rem",
backgroundColor: color.black[200],
color: color.white[100],
border: "none",
borderRadius: "8px",
...typographyVars.body2,
marginBottom: "2rem",
});

export const scrollContent = style({
marginTop: "2rem",
});

export const paragraph = style({
...typographyVars.body3,
color: color.gray[100],
marginBottom: "1rem",
});

export const sheetTitle = style({
...typographyVars.caption1,
color: color.black[200],
marginBottom: "1rem",
});

export const sheetDescription = style({
...typographyVars.body3,
color: color.gray[100],
marginBottom: "2rem",
});

export const sheetContent = style({
display: "flex",
flexDirection: "column",
gap: "1rem",
});

export const sheetItem = style({
padding: "1.6rem",
backgroundColor: color.white[200],
borderRadius: "8px",
...typographyVars.body3,
color: color.black[200],
border: `1px solid ${color.gray[300]}`,
});
46 changes: 46 additions & 0 deletions src/shared/components/bottom-sheet/test/bottom-sheet-test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// 바텀시트 테스트용
// 추후 삭제 예정

import { useState } from "react";
import BottomSheet from "../bottom-sheet";
import * as styles from "./bottom-sheet-test.css";

const BottomSheetTest = () => {
const [isOpen, setIsOpen] = useState(false);

const handleOpen = () => setIsOpen(true);
const handleClose = () => setIsOpen(false);

return (
<div className={styles.container}>
<h1 className={styles.title}>BottomSheet Test Page</h1>
<button className={styles.openButton} onClick={handleOpen}>
바텀시트 열기
</button>

<div className={styles.scrollContent}>
{Array.from({ length: 50 }, (_, i) => (
<p key={i} className={styles.paragraph}>
뒷배경 스크롤 테스트 {i + 1}
</p>
))}
</div>

<BottomSheet isOpen={isOpen} onClose={handleClose}>
<h2 className={styles.sheetTitle}>바텀시트 제목</h2>
<p className={styles.sheetDescription}>
아래로 드래그하거나 뒷배경을 탭하면 닫힘
</p>
<div className={styles.sheetContent}>
{Array.from({ length: 30 }, (_, i) => (
<div key={i} className={styles.sheetItem}>
{i + 1}
</div>
))}
</div>
</BottomSheet>
Comment on lines +29 to +41
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

      <BottomSheet>
        <BottomSheet.Trigger>
          <button>바텀시트 열기</button>
        </BottomSheet.Trigger>
      
        <BottomSheet.Content>
          <BottomSheet.Header>바텀시트 제목</BottomSheet.Header>
          <BottomSheet.Description>
            아래로 드래그하거나 뒷배경을 탭하면 닫힘
          </BottomSheet.Description>
          . . .
        </BottomSheet.Content>
      </BottomSheet>

요런 인터페이스를 가지면 느좋일듯해요.
읽기도 쉬워지고(책임을 명확히함), 변경에도 유연하고, 외부에서 오픈여부를 관리하고 알고있을 필요도 없어지고, ... 등등
@jstar000

Copy link
Copy Markdown
Member Author

@jstar000 jstar000 Nov 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

하 이까지 찾아와주셔서 리뷰까지 남겨주시고 감사합니다ㅠㅠ

작성해주신 Compound Component 패턴도 미나미미나때 다뤘었던 내용이랑 밀접한 관련이 있었네요 '어떻게' 대신 '무엇을' 할 지 집중할 수 있도록 해주는 선언적인 구현 방식!

현재

  • 바텀시트 사용처에서 open상태, open/close 핸들러 등을 관리해야 함
  • 사용 시 뭐가 헤더인지, 뭐가 본문인지 등 파악이 힘듦
    => 바텀시트가 어떻게 동작하는지를 사용처가 알아야 함

Compound 방식

<BottomSheet>
  <BottomSheet.Trigger>
    <button>열기</button>
  </BottomSheet.Trigger>

  <BottomSheet.Content>
    <BottomSheet.Header>제목</BottomSheet.Header>
    <BottomSheet.Description>설명</BottomSheet.Description>
    <div>본문</div>
  </BottomSheet.Content>
</BottomSheet>

=> 사용처에서는 무엇을 렌더링할 지만 선언하면 됨
상태 관리를 Compound 컴포넌트 내부로 캡슐화하고 사용할 때는 구조만 선언하면 되므로 바텀시트 내부 로직이 바뀌어도 사용처에서는 수정할 필요가 없어 변경에도 유연해지네요.

=> 바텀시트 내부 동작(드래그, 애니메이이션 로직, 이벤트 핸들링 등)은 절차적일 수밖에 없으므로 절차적으로 구현, 사용처에서는 Compound Component 등 적절한 구현방식을 사용해 절차적인 내부 동작을 감추고 깔끔한 인터페이스만 노출하도록 수정해보겠습니다! 합세 끝나고 꼬옥 적용해볼게요 지금 수정하면 API 연결 못할 듯...ㅋㅋㅋ


미미나 듣고 어떤 상황에서 선언적으로 구현하는게 적합할지, 어떤 상황에서 절차적으로 구현하는게 적합할지에 대해서 합세 코드 다시 보면서 고민해봤는데, 합세에서 담당한 컴포넌트 지연로딩 구현은

<LazySection fallback={<LoadingFallback />}>
  <ProductDetail />
</LazySection>

이렇게 구현해 사용하는 쪽에서 선언적으로 사용할 수 있도록 구현, 내부로직은 절차적으로 구현한 것 같아요. 사실 선언적/절차적을 의식하고 구현한 건 아니었는데 미미나 듣고, 달아주신 리뷰 확인하고, 구현한 코드 다시 확인해보니 눈에 들어오네요ㅎㅎ 어떻게 구현해야할 지에 대해 큰~~ 깨달음을 주셨습니다 강민하사랑해

그리고 선생님저 질문이 생겼는데

  • 미미나때 useQuery?가 내부적으로는 몇백줄의 절차적인 코드로 구현됐다고 얘기하셨는데, 저수준 로직은 본질적으로 절차적이니까 그렇게 구현될 수밖에 없는게 맞을까요..?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

미미나때 useQuery?가 내부적으로는 몇백줄의 절차적인 코드로 구현됐다고 얘기하셨는데, 저수준 로직은 본질적으로 절차적이니까 그렇게 구현될 수밖에 없는게 맞을까요..?

그쵸. 결국에 우리가 어떤 코드를 써도 결국엔 컴퓨터(cpu)의 절차적인 명령어로 처리가 됨.
예를 들면 많이들 작성해봤을 axios.interceptors 이런거는 필연적으로 절차적인 로직으로 작성될 수 밖에 없어요.ㅎㅎ
결국에 어디까지 로직을 숨기고 드러낼지가 핵심이에여(적절한 추상화)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

아하 이해했어요 앞으로 선언적/절차적 신경쓰고 추상화 레벨, 책임 분리, 캡슐화 고려하면서 구현해보겠습니다ㅎㅎ 리뷰 감사합니다~~

</div>
);
};

export default BottomSheetTest;
Loading