Skip to content
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

[FE] 반응형 도입 #163

Merged
merged 3 commits into from
Dec 23, 2023
Merged

[FE] 반응형 도입 #163

merged 3 commits into from
Dec 23, 2023

Conversation

nlom0218
Copy link
Contributor

@nlom0218 nlom0218 commented Dec 18, 2023

반응형 도입을 위한 작업을 진행했습니다. 내용이 많지 않아 아래의 설명으로 이번 작업에 대한 내용을 파악하실 수 있을거에요.

1. 기기에 따른 viewport 상수화

기기에 따른 viewport를 상수화했습니다.

export const MEDIA_SIZE = {
  mobile: 768,
  tablet: 1023,
};

src/constant/media에서 확인할 수 있어요. 사이즈 기준은 여러 글을 참고하여 가장 많이 사용하는 너비로 정했습니다. 이 부분은 추후에 여러 의견을 듣고 수정할 수 있어요.

2. media query 도입

현재 저희가 데스크탑 기준으로 스타일링을 하고 있습니다. 때문에 테블릿, 모바일 일 때의 스타일링을 추가해야 하는데요. 이를 위해 미디어 쿼리를 도입해야 합니다. 스타일 컴포넌트에서는 다음과 같이 미디어 쿼리를 사용할 수 있어요.

export const Layout = styled.div`
  background-color: blue;

  @media (max-width: 768px) {
    background-color: red;
  }
`;

@media (max-width: 768px)을 작성하고 스타일링을 하면 되는데요. 해당 구문의 의미는 가로의 너비가 최대 768px 일 때 까지 다음의 css를 적용해라입니다. 즉, 최대 너비가 768px보다 크면 적용이 안되고 그 이하일 때만 적용이 됩니다. 한 문장으로 설명하면 768px 이하일 때 적용되는 css입니다. 여전이 미디어 쿼리에서의 max, min은 헷갈리네요

무튼 위와 같이 작성하여 각 페이지에서 가로 사이즈에 따른 스타일링을 하시면 됩니다. 하지만 매번 @media (max-width: 768px)을 작성하면 헷갈릴 수도 있고 실수할 수도 있기 때문에 이것도 상수화를 했습니다. src/constant/media에 있습니다. 다음과 같이 말이죠.

export const MEDIA_QUERY = {
  mobile: `@media(max-width: ${MEDIA_SIZE.mobile}px)`,
  tablet: `@media(max-width: ${MEDIA_SIZE.tablet}px)`,
};

여기서 MEDIA_SIZE은 위 기기에 따른 viewport입니다. 이 상수화된 값을 가져와 다음과 같이 사용하면 됩니다.

export const Layout = styled.div`
  background-color: blue;

  ${MEDIA_QUERY.mobile} {
    background-color: red;
  }
`;

3. useMedia

위 훅도 만들었어요. 스타일에서 뿐 아니라 리액트 컴포넌트에서도 기기에 따라(가로 사이즈에 따라) 원하는 동작이나 렌더링되는 컴포넌트가 다를 수 있기 때문이에요. 해당 훅은 전역상태입니다. 전역상태로 만들기 위해 이번에 zustand을 도입했어요. 다른 전역상태 라이브러리도 있지만 zustand가 훨씬 가볍고 사용법고 간단해서 선택했어요. 관심있는 쌤들은 한 번 공부해보세요! 저도 요세 하고 있습니다.

아무튼 위를 통해 가로 사이즈에 따라 useMedia가 반환하는 값이 달라집니다. 3개의 값이 있어요. mobile, tablet, desktop이 그것이에요. 아래는 간단한 사용 예시입니다.

const AComponent = () => {
  const media = useMedia();

  if(media === "mobile") return <div>모바일</div>

  return <div>테블릿, 데스크탑</div>
}

@nlom0218 nlom0218 linked an issue Dec 18, 2023 that may be closed by this pull request
@nlom0218 nlom0218 self-assigned this Dec 18, 2023
@nlom0218 nlom0218 added the feature 기능 label Dec 18, 2023
[setMedia],
);

useLayoutEffect(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

);

useLayoutEffect(() => {
const observer = new ResizeObserver(observerBodySize);
Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

@jjswan jjswan left a comment

Choose a reason for hiding this comment

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

다른 게시글에서 봤던 반응형 컴포넌트는 엄청 간단한 형태였다는 것을 알게 되었습니다. useLayoutEffect나 ResizeObserver 모두 처음 봐서 신기했습니다. 고생하셨습니다!


const useMedia = () => {
const media = useMediaStore((state) => state.media);
const setMedia = useMediaStore((state) => state.setMedia);
Copy link
Contributor

Choose a reason for hiding this comment

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

useMediaStore을 이용해서 현재의 미디어 상태를 가져오고 useMediaStore을 이용해서 미디어 상태를 설정하는 함수를 가져오는군요.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

네! Store라는 저장소를 바로 컴포넌트에서 사용하는 것보다 useMedia처럼 커스텀 훅을 만들어 여러 기능을 추가하고 사용하는 것이 좋은(?) 패턴이라고 하더라고요!


if (!body) return;

observer.observe(body);
Copy link
Contributor

Choose a reason for hiding this comment

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

이 부분이 크기 변화 감시 역할을 하는군요. 'observer.observe가 body의 사이즈 변화 감지 -> 변화가 있다면 observerBodySize를 이용하여 ResizeObserverCallback 함수 실행하여 크기 재측정' 의 순서로 이뤄지는 게 맞을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

네! 맞습니다 :) ResizeObserver API를 활용하여 body의 사이즈를 항상 감지하고 있어요. 사이즈가 변화될 때 마다 observerBodySize을 호출하게 되고요!

@nlom0218 nlom0218 merged commit 018b801 into develop Dec 23, 2023
@nlom0218 nlom0218 deleted the 162-fe-반응형-도입 branch December 23, 2023 11:35
Copy link
Contributor

@seoltang seoltang left a comment

Choose a reason for hiding this comment

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

반응형을 위한 훅이라니 신선하네요! 👍 아무래도 스타일을 js로 직접 변경하는 것은 계산부담이 커질 수 있으니 반응형 레이아웃 변경은 웬만하면 styled-components 내에서 해결하고 스크롤이라든지 정말 기능적으로 반응형에 대한 로직이 필요할 경우에만 이 media 상태를 사용하는 것이 좋을 것 같군요 ㅎㅎ
++ PR 자세히 작성해주셔서 이해가 아주 잘 됐습니다 👍👍

Comment on lines +1 to +16
import { create } from 'zustand';

type Media = 'mobile' | 'tablet' | 'desktop';

type State = {
media: Media;
};

type Action = {
setMedia: (media: Media) => void;
};

const useMediaStore = create<State & Action>((set) => ({
media: 'desktop',
setMedia: (media) => set(() => ({ media })),
}));
Copy link
Contributor

Choose a reason for hiding this comment

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

zustand는 안 써봤는데 recoil이랑 비슷하고 간단한 것 같네요! 저도 공부하면서 써볼게요 👍

Comment on lines +6 to +9
export const MEDIA_QUERY = {
mobile: `@media(max-width: ${MEDIA_SIZE.mobile}px)`,
tablet: `@media(max-width: ${MEDIA_SIZE.tablet}px)`,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

마음이 편안해지는 상수화,, 아주 좋습니다 👍 저도 이거 사용해서 반응형 만들어볼게요 :D

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature 기능
Projects
None yet
Development

Successfully merging this pull request may close these issues.

[FE] 반응형 도입
3 participants