[바닐라코딩 Bootcamp] week6 feedback
styled-components에 대한 활용
react에만 집중해서 과제를 하다보니 styled-components는 단순히 컴포넌트의 스타일을 지정하는 정도로만 사용했다.
역시나 멘토님께서 해당 부분에 대해 코멘트를 남겨주셨다. 더 찾아보고 적용해보았어야 했는데라는 생각이 들었다.
글로벌 스타일 지정(createGlobalStyle), theme, styled-component의 확장 등등 활용법이 굉장히 많다는 것을 배웠다.
1) 전역 스타일 지정
구현된 코드에는 HTML 파일에 style sheet를 지정해서 전역 스타일을 지정해주었는데, styled component를 사용하면 아래와 같이 사용할 수 있다.
import { createGlobalStyle } from 'styled-components'
const GlobalStyle = createGlobalStyle`
body {
color: ${props => (props.whiteColor ? 'white' : 'black')};
}
`
// later in your app
<React.Fragment>
<GlobalStyle whiteColor />
<Navigation /> {/* example of other top-level stuff */}
</React.Fragment>
GlobalStyle 컴포넌트 또한 ThemeProvider를 통해서 theming 전략을 사용할 수 있다.
import { createGlobalStyle, ThemeProvider } from 'styled-components'
const GlobalStyle = createGlobalStyle`
body {
color: ${props => (props.whiteColor ? 'white' : 'black')};
font-family: ${props => props.theme.fontFamily};
}
`
// later in your app
<ThemeProvider theme={{ fontFamily: 'Helvetica Neue' }}>
<React.Fragment>
<Navigation /> {/* example of other top-level stuff */}
<GlobalStyle whiteColor />
</React.Fragment>
</ThemeProvider>
코드 출처 - https://styled-components.com/docs/api#createglobalstyle
styled-components는 ThemeProvider를 통해서 강력한 theming 전략을 제공한다고 한다.
ThemeProvider는 context를 활용해서 모든 리액트 컴포넌트에 theme 속성을 전달할 수 있다고 한다.
특정 컴포넌트의 depth가 아무리 깊어도 root에 ThemeProvider이 있다면 모든 렌터 트리의 자식은 theme 속성을 갖게 된다.
2) 반복적으로 사용되는 스타일
일관적인 UI를 보여주기 위해 반복적으로 사용되는 Hex코드나 스타일들은 styled components에서 theme으로 지정해서 사용할 수 있다고 한다.
const Main = styled.div`
color: ${(props) => props.theme.red};
`;
// import styled, { ThemeProvider } from 'styled-components'
const Box = styled.div`
color: ${props => props.theme.color};
`
render(
<ThemeProvider theme={{ color: 'mediumseagreen' }}>
<Box>I'm mediumseagreen!</Box>
</ThemeProvider>
)
theme을 사용하면 정해진 규칙 외의 스타일을 사용하게 되는 실수를 줄여주고 각 스타일에 네이밍을 해줄 수 있어서 컴포넌트 각 요소에 어떤 스타일이 사용되었는지 파악하기도 쉽다.
styled-component 사용 예시
https://react.vlpt.us/styling/03-styled-components.html
Infinite Scroll을 Intersection Observer API를 이용해서 구현해보기
Intersection Observer를 사용하면 무한 스크롤을 구현할 때 사용하는 debounce 나 throttle을 사용하지 않아도 된다는 장점이 있고 무엇보다 offset 값을 구하기 위해 매번 레이아웃을 새로 그리는 reflow를 하지 않는다는 장점이 있다고 한다.
reflow가 무엇인지, 왜 reflow를 하지 않는 것이 장점인지, reflow를 최소화하는 방법으로 어떤 것들이 있는지 알고 있는 것이 중요하다라고 조언을 해주셨다.
데이터를 받아올 때 에러가 발생하거나 데이터가 없을 시 대응하는 코드 필요
API를 호출해서 데이터를 받아올 때 에러가 발생하거나 비디오가 없을 때, 사용자가 어떤 상황인지 가늠할 수 없기 때문에 대응하는 코드가 필요할 것 같다.
컴포넌트의 재사용성
어제 테스트 코드를 작성하다가 구현해둔 컴포넌트들이 재사용성도 좋지 않고, 테스트 하기도 불편하다는 생각이 들어서 컴포넌트를 분리해서 새롭게 구성해보았다. 하지만 다시 작성해서 push를 한 시점보다 리뷰하는 시점이 빨랐는지 이전 코드에 대한 리뷰가 남겨져있었는데 컴포넌트의 재사용성에 대해 고민해보면 좋을 것 같다는 의견을 주셨다. 해당 부분은 이미 리팩토링을 진행한 부분이지만 컴포넌트의 재사용성은 항상 깊게 고민하고 생각하면서 작성해야하기 때문에 블로그에 꼭 남겨두어야겠다는 생각이 들었다.
객체의 초기값 설정
하위의 컴포넌트에서 비구조화 할당으로 값을 활용하기 위해 state의 초기값에 각 key를 설정해주고 null을 초기값으로 설정했었다.
const [video, setVideo] = useState({
id: null,
snippet: {
title: null,
description: null,
publishedAt: null
},
});
이 때 서버에서 기본값이 모두 null로 오지 않는다면 어떤 타입의 데이터가 올지 추측하기 쉽게 작성하는 방법도 있다고 알려주셨다.
const [video, setVideo] = useState({
id: '',
snippet: {
title: '',
description: '',
publishedAt: ''
},
});
하지만 보통 객체 형태의 데이터가 들어오고 특별한 이유가 있는게 아니라면 초기값을 null로 설정해줄수도 있다고 추가로 피드백을 주셨다.
const [video, setVideo] = useState(null);
그 외 코드 스타일, 네이밍에 대한 피드백
1) 인라인으로 작성한다면 중괄호를 생략할 수도 있다.
// before
<EntryWrapper onClick={() => { onClick(id) }}>
// after
<EntryWrapper onClick={() => onClick(id)}>
2) state 네이밍
처음 페이지가 로드 되었을 때만 웰컴 모달을 보여주기 위해 state를 사용하였는데, 변수명을 isInitLoad라고 설정했다.
isInitLoad보다는 모달이 열렸는지 열리지 않았는지 확실하게 나타낼 수 있는 구체적인 변수명이 더 좋을 것 같다고 말씀해주셨다.
예를 들면, isModalOpne, isModalShow 등
3) 함수 네이밍
스크롤 이벤트가 발생했을 때 다음 페이지의 비디오를 가져오는 기능을 하는 함수를 선언했는데 이 부분 역시 조금 더 자연스러운 네이밍을 고민하면 좋을 것 같다고 피드백을 주셨다.
getVideosOnScroll -> getNextPageVideos
4) props 네이밍
보통 prop명은 on을, 핸들러 함수는 handle이라는 단어를 접두사로 사용한다. (onClick(prop) handleClick(handler))
통상적으로 prop명을 짓는 규칙들이 있는데, 예를 들어 lower camel case를 사용하거나 이름을 50자 미만으로 지어 주는 방법 등이 있다. prop 네이밍을 잘 지으면 컴포넌트가 많아지거나 전달하는 prop이 많아질 때 혼란을 방지할 수 있으니 숙지해보는 것이 좋다라고 하셨다.
5) parameter destructuring
parameter destructuring을 사용하여 필요한 데이터만 가져오는 방법도 있다. 더 간결해 보인다는 장점이 있다.
// before
{videos.length > 0 && videos.map((video, index) => {
// after
{videos.length > 0 && videos.map(({ id, snippet }, index) => {
6) 단축 평가 논리 연산자 사용
논리 연산자를 사용하면 코드를 간결하게 나타낼 수 있을 것 같다.
// before
id: video.id.videoId ? video.id.videoId : video.id.playlistId,
// after
id: video.id.videoId || video.id.playlistId,
7) 시간과 관련된 값의 네이밍
시간과 관련된 값의 이름은 보통 ~~edAt으로 작성한다.
ex) publishedAt createdAt updatedAt
8) self-closing
children이 없다면 self-closing을 할 수 있다. 이 부분은 알고 있었는데도 children이 없는지 확인을 못 한 것 같다..