May 1, 2021 • ☕️☕️ 8 min read
React Portal | Context API
콜린이랑 마지막 페어 주간! 내가 주도적으로 개발한 것 같아서, 콜린의 의견이 어떻냐고 자주 물어봤다. 그래도 항상 다 괜찮다고 해주니까, 괜히 너무 걱정하지 말고 그 말을 믿어야겠다. 나는 사실 생각하지 않고 코드를 일단 짜는 스타일인데, 중간중간에 콜린이 사소한 실수들을 잘 잡아줘서 삽질할 뻔 한 시간들을 많이 줄일 수 있어서 좋았다.
전체적으로 무난하고 평화로운(?) 코딩이었다. 시간에 조금 쫓겨서 에러 처리를 완벽히 하지 못한 점이 아쉽다. 언제나 여유가 좀만 더 있었으면, 생각하게 된다.
(정리 중) 요 미키쓰의 테코톡 신기한 툴을 사용했다
자식 컴포넌트에 props로 넘겨주는 부모 컴포넌트의 메소드 내부에서 this
를 참조하지 않고 있다면 바인딩을 안 해줘도 될까? 하는 의문이 문득 들었다. 답은 일단 YES긴 한데, 왜냐면 참조해야할 값이 없으니까. 그렇지만 컴포넌트의 마운트/언마운트 시점을 예측 불가할 수도 있기 때문에 자식 컴포넌트에서 props로 받고 있는 메소드가 this 참조값을 잃어버릴 수도 있겠다는 생각은 든다. 일단 바인딩하자!
부모 컴포넌트의 외부 DOM에 컴포넌트를 렌더링하는 기능이다. 마스킹된 숫자를 입력해야 하는 일부 자식 컴포넌트에서, 현재의 view 상위에서 가상 키보드를 보여줘야 할 상황에서 사용했다.
우선 index.html
의 root
와 같은 층위에, Portal 컴포넌트를 띄울 곳을 id
attribute와 함께 지정해 준다.
<!-- public/index.html -->
<div id="root"></div>
<div id="keyboard"></div>
hook을 사용하고 있기 때문에 React.createPortal
을 사용했다. 첫 번째 인자로는 Portal을 통해 보여줄 컴포넌트, 두 번째 인자는 보여줄 DOM 노드를 전달해준다.
// VirtualKeyboard.js
export const KeyboardPortal = ({ children }) => {
const el = document.getElementById('keyboard');
return createPortal(children, el);
};
const VirtualKeyboard = (props) => {
return (
// ...
);
}
사실 root와 같은 층위에 새로운 DOM 요소를 삽입해주는 것이 부담스러워서(?), Keyboard를 사용하는 상위 React 컴포넌트에 해당 노드를 심어주고자 했으나 ‘Target DOM’을 찾을 수 없다는 에러가 계속 발생했다.
리뷰를 통해 에러의 원인을 확실히 할 수 있었다.
document.getElementById
로 셀렉팅 해올 수 있는 경우는 DOM 오브젝트가 생성된 이후입니다. 리액트 컴포넌트의 렌더링 순서에 의존하는 코드를 작성하면 그 코드는 잘못 작성된 것
사용하는 곳에서는 Portal 컴포넌트로 실제 렌더링할 컴포넌트를 한번 감싸준다.
// CardPasswordInput.js
return (
<KeyboardPortal>
<VirtualKeyboard inputNumbers={cardPas sword} setInputNumbers={setCardPassword} />
</KeyboardPortal>
)
Ref
React를 사용하다보면 컴포넌트의 요소들을 조립하기가 난감해지는 상황이 종종 발생한다. 이번에는 form
과 button
을 연결하고 싶은데, button
이 컴포넌트 상으로 form
과 같은 컴포넌트 내에 위치하기가 어려워졌다.
이럴 때!
button
의 form
속성을 활용하면 멀리 떨어진 form
과 연결할 수 있다. React에서는 formId
속성을 사용한다.
<form id="register">
<input />
</form>
<button form="register">
prop-types
를 통해 vanilla JS를 사용하면서도 React 컴포넌트에 들어가야 할 props에 대해 타입 제한을 걸 수 있다.
PageHost.propTypes = {
navigationTitle: PropTypes.string.isRequired,
hasBackButton: PropTypes.bool.isRequired,
children: PropTypes.node.isRequired,
handleGoBack: PropTypes.func,
};
그런데 nesting된 object가 들어올 경우 타입을 뭐라고 명시해줘야 할까?
PropTypes.shape()
를 쓰면 된다!
CardCompletion.propTypes = {
cardData: PropTypes.shape({
bankId: PropTypes.string.isRequired,
cardNumbers: PropTypes.object.isRequired,
expirationDate: PropTypes.object.isRequired,
ownerName: PropTypes.string.isRequired,
}),
handleConfirmPage: PropTypes.func.isRequired,
isEditing: PropTypes.bool.isRequired,
cardId: PropTypes.string,
};
재사용되는 컴포넌트의 크기를 페이지마다 다르게 하다보니, 사이즈의 정확한 픽셀값보다 상대적인 값을 사용하기로 했다.
em
은 실제 사용된 요소의 폰트 크기에 의해서 정해진다.
html {
font-size: 100%; /* 16px by default */
}
h1 {
font-size: 2em; /* 16px * 2 = 32px */
}
rem
은 루트의 em을 의미한다. 1rem은 브라우저에서 설정된 <html>
의 font-size
와 동일하다.
Ref https://jeongwooahn.medium.com/번역-rem-vs-em-5eac6122b8ea
hidden
global attribute인 hidden
attribute는 해당 element가 아직 관련이 없거나, 더 이상 관련이 없는지를 나타내는 boolean attribute이다. 예를 들어, 현재 상황과 관련없는 페이지의 elements를 숨기는데 사용될 수 있다. 브라우저는 hidden
attribute가 설정된 elements를 설정하지 않는다.
<p hidden>This node won't be rendered.</p>
hidden attribute는 단순히 어떤 요소를 표시하지 않기 위해 사용하면 안 된다. 만약 어떤 것이 hidden으로 설정되면, 스크린 리더기 등 모든 표현방식에서 사라지게 된다. 단순히 보이지 않게 하고 싶다면, css의 display: none
을 사용하자.
css의 display: none
과 같이 html element의 스타일 속성을 명시적으로 지정한 경우에는, hidden 속성이 있더라도 이를 override한다.
Ref https://developer.mozilla.org/ko/docs/Web/HTML/Global_attributes/hidden https://velog.io/@bigsaigon333/Level1-javascript-racingcar
패턴에 막혀서 대부분의 시간을 당황해 하며 보냈다. 😭 역시 인생에 예측대로, 뜻대로 되는 건 없다!
비즈니스 로직 Service 단에 작성하는 코드 비즈니스 흐름 기준으로 각 역할을 가진 협력 도구 클래스들을 중계해 주는 역할을 하며, 각 협력 도구 클래스들이 명시적으로 한 가지 일을 담당한다.
상세 구현 로직은 잘 모르더라도 비즈니스의 흐름은 이해 가능한 로직이어야 한다.
모듈화 새로운 기술 또는 기술의 전환이 필요할 때 비교적 쉽고 빠르게 기존 비즈니스에 가장 적은 영향을 끼치며 모듈 단위로 변경이 가능해지게끔 구성
👾 통제와 제어를 통해 소프트웨어를 예측 가능하게 만들자. 지속 성장하면서 운영 가능한 소프트웨어를 위해서!
Ref https://geminikim.medium.com/지속-성장-가능한-소프트웨어를-만들어가는-방법-97844c5dab63
Ref
iOS - 손쉬운 사용 크롬 익스텐션 - WAVE
밀리고 밀린다..! 사람 살려!!