(21.07.27) material-ui 스타일 && react-router-dom with TS
도깨비젤리
·2021. 7. 27. 22:35
작은 지식이라도, 하루에 하나씩.
한 줄 요약
🦄TypeScript에서 react-router를 사용할때, match / history / location 객체를 사용하기 위해서는 RouteComponentProps를 import 해야한다.🦄
🦄만약, match의 params에 넣어서 전달해줄 props가 있다면, 그 props의 interface도 설정해줘야한다 🦄
🦄material-ui 적용환경에서 css를 먹이는 방법은
1.inline style 적용
2. makeStyles hook 사용,
3.styled component 라이브러리 사용🦄
RouteComponentProps
React를 TS로 짜면서 제일 거지 같은 점은 역시 타입 지정을 안해주면 에러를 가차없이 뱉는다는 점이다. 타입체크를 통한 에러 감소와 개발자의 편의증대가 React with TS의 장점이라고는 하는데, 나 같은 코찔이한테는 키보드의 수명을 단축시키게 하는 요소인거 같다.
그래도 꼴에 쫀심은 있어서 계속 모르는데로 머리 박고 있긴 한데, 이번에 발생한 RouteComponentProps 에러는 상당히 골치 아팠다.
- 에러 발생 코드
<nav>
<Link to="/">메인 화면</Link>
{Boolean(userName) ? (
<Link to={`/profile/${userName}`}>프로필 화면</Link>
) : (
<Link to="">암것도 아님</Link>
)}
.... 중략
<Route path="/profile/:username" component={Profile} />
링크를 클릭하면 /profile 뒤에 붙은 문자를 username이라는 이름으로 params에 담아서 Profile 컴포넌트로 보내준다
const Profile: React.FC = ({ match }) => { //error!!! wtf!!
console.log(match);
console.log(match.params);
console.log(match.params.username);
... 생략
이후 항상하던대로 상속받은 params 자리에서 match 객체를 구조 분해 할당으로 꺼냈는데...
갑자기 match에 빨간줄이 드르륵 그어지면서
{ children?: ReactNode; }' 형식에 'match' 속성이 없습니다
라는 에러를 뱉었다.
1차 구글링 한 결과, react-router-dom을 타입스크립트 환경에서는 역시 타입이 필요합니다. router를 가져다 쓰는 컴포넌트에서는 RouteComponentProps를 제공해서 사용해야한다.
RouteComponentProps는 다음과 같이 구성되어있다.
export interface RouteComponentProps<
Params extends { [K in keyof Params]?: string } = {},
C extends StaticContext = StaticContext,
S = H.LocationState
> {
history: H.History<S>;
location: H.Location<S>;
match: match<Params>;
staticContext?: C | undefined;
}
보아하니, Router의 요소들인 history, location, match의 타입이 지정되어있다.
좋다 그럼, RouterComponentProps를 가져다 쓰자.
- 1차 수정 코드
const Profile: React.FC<RouteComponentProps> = ({ match }) => {
console.log(match);
console.log(match.params);
console.log(match.params.username); //error!!!!! wtf!
... 생략
역시 타입스크립트 이새기는 만만하지 않다. 이번 에러는 다음과 같다.
{}' 형식에 'username' 속성이 없습니다.
이번 에러는 왜 발생한거냐면, RouteComponent의 match 객체는 다른 친구들과 다르게 추가로 <Params>라는 타입을 받는다. 근데, 여기에 따로 타입을 지정해주지 않아서 <Params>는 텅 비어 있는 { } 객체가 되어버린 것이다. 이런 상황에서 console.log(match.params.username)을 찾으려고 봤는데, 아무것도 없으니까 TS는 아무것도 없는데 뭘 출력하라고 하는거냐 하면서 에러를 뱉은 것이다.
그래서 우리는 Params에 대한 타입을 또 다시 만들어줘야한다.
- 최종 코드
interface paramsProps {
username: string;
}
const Profile: React.FC<RouteComponentProps<paramsProps>> = ({ match }) => {
console.log(match);
console.log(match.params);
console.log(match.params.username); // 도깨비젤리
그러자 드디어 에러 없이 해-피한 결과물을 확인할 수 있었다.
이거 에러 잡느라고 머리털 한 무더기는 빠진거 같다.
Material-ui에서 스타일 적용
Material-ui도 머리 아픈 친구이다. React에서 가장 많이 쓰는 디자인 프레임워크라서 React 프로젝트에서 우선순위로 데려오는 친구인데, 아 이 친구 스타일 입히는거 쉽지 않다.
정확히 말하자면외부에서 css/ scss 파일 불러와서 입히는 건 어려움이 없지만, makeStyle Hook이나 inline Style을 적용하는데에 많은 애로사항이 있다.
쉬운게 있으면 쉬운걸로 하지 왜 그러냐?? 라고 묻는다면, 모든 상황에 들어맞는 황금열쇠는 없다고 말해주고 싶다.
이번에 제작하는 컴포넌트는 유저의 Viewport에 따라 크기를 조절해야하는데, 나는 유저의 Viewport를 window.innerWidth로 구했다. 하지만 이건 JS문법이기 CSS에서는 사용할 수 없다. (설마 되나?? 되면 많이 억울할거 같은데)
그래서 CSS-in-JS의 대표 주자인 makeStyles Hook과 React inline Styling 기법을 사용해서 CSS를 입혀봤다.
이 두 기법을 사용하면서 배운 점을 다시 한 번 정리해보려 한다.
여러개의 ClassName을 사용하고 싶을 때는?? (makeStyles Hook)
const useStyles = makeStyles<StyleProps>(() =>
createStyles({
color: {
color: "red",
},
font: {
fontSize: "23rem",
},
})
);
... 중략
const classes = useStyles();
... 중략
/*클래스 이름을 하나만 넣을 때는 그냥 써도 되지만*/
<h2 className={classes.test}>클래스 하나</h2>
/*클래스 이름을 2개 이상 사용할 때는 아래와 같이 백틱으로 감싸서 변수처리해서 넣어야한다*/
<p className={`${classes.test} ${classes.font}`}>클래스 두개 이상 </p>
makeStyles Hook을 사용할 때, 한 태그에 여러개의 클래스 이름을 사용해야한다면, 위와 같이 백틱으로 감싸서 변수로 인식할 수 있게 보내줘야한다.
inline Style을 적용할 때 주의점 && 스마트하게 하는 방법
inline Style의 핵심은, style 요소를 잔뜩 담아놓은 nested된 객체를 여러개 만드는 것이다.
여기서 포인트는, 하나의 객체가 곧 하나의 스타일 뭉덩이(??)가 되며, 이를 speard 문법을 사용해 컴포넌트에 적용한다.
실제 코드를 보면서 확인해보자
...전략
const baseWidth = window.innerWidth > 1280 ? 1280 : window.innerWidth * 0.9;
const diagonalWidth = baseWidth * 0.3;
const boxWidth = baseWidth * 0.99;
const imgAreaWidth = baseWidth * 0.99;
const upperHeight = baseWidth * 0.19;
const imgAreaHeight = diagonalWidth + upperHeight;
const styles = {
background: {
position: "relative" as "relative", // 이 뭔...이라는 생각이 드는데 TS의 규칙입니다
width: "0",
height: `${upperHeight}px`,
margin: "0 auto",
borderBottom: `${diagonalWidth}px solid red`,
borderLeft: `${imgAreaWidth}px solid transparent`,
},
image: {
position: "absolute" as "absolute", // wtf2
right: "0",
width: `${imgAreaWidth}px`,
height: `500px`,
alt: "asd",
"z-index": "-1", // z-index는 "-"이 들어가기 때문에 따옴표로 꼭 감싸줘야한다
},
};
return (
...중략
<div style={{ ...styles.background }}>
<img
src="https://i.ytimg.com/vi/C_duDk5e8yU/maxresdefault.jpg"
alt="ase"
style={{ ...styles.image }}
...후략
></img>
styles라는 변수를 css 문법에 맞게 key-value 꼴로 설계한 다음, 이후 return부에서 구조분해할당 (spread 문법)을 사용하여 스타일을 이쁘게 입혀주면 된다.
⛔Typescript를 사용하고 있다면 주의해야할 점들⛔
- position 속성을 지정해줄 때, 그냥 static, relative, absolute로 쓰는 것이 아니라, "static" as "static" , "relative" as "relative" , "absolute" as "absolute"로 써야한다. TS 내부적으로 static, relative, absolute 을 그냥 string으로 인식하기 때문이라고 하는 거 같은데, 자세히는 모르겠다...
- z-index와 같이, 속성에 특수문자가 들어가는 친구들은 반/드/시 따옴표로 감싸야한다.
읽을거리
https://d0gf00t.tistory.com/22
왜 CSS-in-CSS가 아니라 CSS-in-JS을 해야하는지 잘 알려주는 글이다.
https://velog.io/@madpotato1713/HTMLCSS-%EB%8C%80%EA%B0%81%EC%84%A0-%EA%B7%B8%EB%A6%AC%EA%B8%B0
이번 스타일링 단계에서 난 에러가 전부 삼각형 하나 그리려고 난 것이다. 이런데도 나중에 까먹으면 굉장히 억울할 것이니, 따로 읽기 목록으로 빼었다.
'TIL' 카테고리의 다른 글
(21.08.01) SweetAlert2 (0) | 2021.08.01 |
---|---|
(21.07.30) redux-persist 사용하기 (0) | 2021.07.30 |
(21.07.21) JS 구조분해 할당 && terminal과 shell의 차이 (2) | 2021.07.21 |
(21.07.21) React && firebase로 클론 코딩 완료 (0) | 2021.07.21 |
(21.07.19) CRA로 만든 react의 webpack 수정하기 (with CRACO) (2) | 2021.07.19 |