우리는 이제 빙액트를 구현하였는데 이제 css를 다룸에 있어서 styled-component를 사용하고자 했다. 그 이유는 애초에 리액트에서의 개발환경을 다 가져가고 싶기 때문이 가장 큰 것같다. 또 어떻게 구현됐을지 궁금하고 재밌기도 하고..
※ 주의: 내 생각대로 구현하였으며 기존의 기능과 크게 다를 수 있음
먼저 styled-component의 동작방식을 보면 class를 랜덤한 값으로 지정해준다. 또 이 클래스를 css로 넣어줌으로써 스타일이 적용된다. 이를 활용해 js에서 css작성 클래스를 지정해 넣어보자.
먼저 styled-component를 적용해 렌더를 해보면 type에서 에러가 나는 것을 볼 수 있다.
이 에러가 발생하는 이유는 우리는 지금 createElement에서 type을 그대로 받아와 사용하기 때문인데
function createElement(type, props, ...children) {
return {
type,
props: {
...props,
children: children.map((child) => (typeof child === 'object' ? child : createTextElement(child))),
},
};
}
styled-component를 적용한 후엔 object 형태로 들어오는 것을 확인 할 수 있다. 이를 판단해 이 element가 스타일 컴포넌트로 생성된 요소인지 아닌지 판단할 때 사용할 것이다.
function createElement(type, props, ...children) {
const isStyledComponent = typeof type === 'object';
if (isStyledComponent) {
...
}
...
}
이 오브젝트를 천천히 뜯어보면
componentStyle로 스타일의 세부내용을 전해주는것을 볼 수 있고 이를 스타일 태그안에 넣어줄 것이다.
먼저 스타일을 전역에서 저장할 변수를 만들고 모든 스타일을 저장해준다. 그리고 head 태그에 style 태그를 넣어서 우리가 만든 만들어준 스타일을 모두 넣어준다. 물론 styled-component 외에도 직접 구현한 클래스가 있을 경우도 빠트리지 않고 구현해준다.
전체 코드
let styledObj = {};
const style = document.createElement('style');
document.getElementsByTagName('head')[0].appendChild(style);
function createElement(type, props, ...children) {
const isStyledComponent = typeof type === 'object';
if (isStyledComponent) {
const componentStyled = type.componentStyle;
const styledClassName = componentStyled.componentId;
styledObj[styledClassName] = componentStyled.rules[0];
// style.type = 'text/css';
let styleCSS = '';
for (const key in styledObj) {
styleCSS += `.${key} {${styledObj[key]}}\n`;
}
style.insertAdjacentHTML('beforeend', styleCSS);
// stlyed외에 직접 클래스 추가한 경우
const className = props?.className ? props.className + ' ' + styledClassName : styledClassName;
props = {
...props,
className,
};
}
return {
type: isStyledComponent ? type.target : type,
props: {
...props,
children: children.flat().map((child) => (typeof child === 'object' ? child : createTextElement(child))),
},
};
}
또 직접 지정해주는 style에 props의 예외 처리도 해준다.
...
const isStyle = (key) => key === 'style';
const isProperty = (key) => key !== 'children' && !isEvent(key) && !isStyle(key);
...
function updateDom(dom, prevProps, nextProps) {
...
const prevStyle = prevProps.style || {};
const nextStyle = nextProps.style || {};
// Remove old styles
Object.keys(prevStyle)
.filter(isGone(prevStyle, nextStyle))
.forEach((name) => {
dom.style[name] = '';
});
// Set new or changed styles
Object.keys(nextStyle)
.filter(isNew(prevStyle, nextStyle))
.forEach((name) => {
dom.style[name] = nextStyle[name];
});
...
}
이렇게 styled-component를 사용하여 스타일은 정상적으로 구현되는 것을 볼 수 있다.
다만 styled-component에서 props를 받지는 못한다. 이를 해결하고자 useEffect를 구현하는데 성공하고 이를 사용해 props를 받아와보고자 했지만 실패했다. 그리고 빙액트의 성능 또한 점점 이슈들이 생기며 의심이 가기 시작했다. 이런식으로는 3주라는 프로젝트 기간내에 빙액트를 활용해 프로젝트까지 완성해야하는 일정속에서 프로젝트를 정상적으로 진행할 수 없다는 판단하에 팀원과 의논을 한끝에 리액트로 리빌딩을 하고 웹팩과 타입스크립트 설정을 직접하는 방향으로 정했다.
많이 아깝고 아쉽지만 실패는 아닌 성장을 위한 밑거름이라 생각해보려 한다.