(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를 사용하고 있다면 주의해야할 점들⛔

 

  1. position 속성을 지정해줄 때, 그냥 static, relative, absolute로 쓰는 것이 아니라, "static" as "static" , "relative" as "relative" , "absolute" as "absolute"로 써야한다. TS 내부적으로 static, relative, absolute 을 그냥 string으로 인식하기 때문이라고 하는 거 같은데, 자세히는 모르겠다...
  2. z-index와 같이, 속성에 특수문자가 들어가는 친구들은 반/드/시 따옴표로 감싸야한다. 

 

 

읽을거리


https://d0gf00t.tistory.com/22

 

[번역] CSS-in-JS에 관해 알아야 할 모든 것

원문: All You Need To Know About CSS-in-JS All You Need To Know About CSS-in-JS - By Please checkout a related story below... hackernoon.com 요약: 컴포넌트로 생각하기— 더이상 스타일시트의 묶음을..

d0gf00t.tistory.com

왜 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

 

[HTML/CSS] 대각선 그리기

html과 css를 배우면서, 참으로 기능이 많다고 생각했다.그런데, 의외로 대각선 그리기가 없는 것이었다!그래서 필자가 알아본 대각선 그리기 몇 가지를 공유한다.우선, 아래 코드는 index.html이다1\

velog.io

 

이번 스타일링 단계에서 난 에러가 전부 삼각형 하나 그리려고 난 것이다. 이런데도 나중에 까먹으면 굉장히 억울할 것이니, 따로 읽기 목록으로 빼었다.