원티드 X 코드스테이츠 프리온보딩 프론트엔드 과정 기업과제 4번
담당했던 부분 : 전체페이지 Grid 구현 / Loading Indicator 제작
✨ 주요 기능
시간이 훌쩍 가서 마지막 프로젝트! 이번 프로젝트에서는 더더욱 맡은 부분이 크지 않았다. 나는 기본적으로 동떨어져 있는 컴포넌트 하나를 만들거나 전체적인 그리드 부분을 잡거나 하는 걸 잘 하고 흥미로워 한다는 것을 짧은 프로젝트들 여러 개를 해 보면서 배워나갔던 시간이었던 것 같다.
이번에도 로딩인디케이터를 만들면서 키프레임과 여러가지 transition에 대해 좀 더 학습하는 시간이 되었던 것 같고, 전체적인 그리드를 잡는 부분에서도 더 학습하게 되었다.
협업은 언제나 중요하지만 전체적인 틀만 잡고 가느냐 아니면 세세한 부분까지도 일단 이야기 해보고느냐에서 차이가 많이 난다는 것도 배운 시간이었다. 이번 프로젝트는 모바일로 제작되어 있는 환경의 서비스를 웹페이지로 만들어보라는 것이었는데 모바일은 참고를 해서 디자인을 하면 되지만 웹 쪽은 팀원들과 이야기를 나누어 배치를 어떻게 해야 할지 등등을 이야기 해야 했다.
모바일 같은 경우에는 가로가 좁고 세로가 긴 형태이니까 새로올라왔어요 와 상세리스트가 row 형태로 되어야 하지만, 웹의 경우에는 가로가 길고 세로가 짧은 형태이니 이 둘이 column 형태로 바뀌는 식이 되었다. 이 과정에서 그리드를 잡을 때 내가 맡은 파트에서만 하면 되는 게 아니라 이 안에 컨텐츠를 끼워넣게 되니까 컨텐츠를 맡은 팀원들에게 '패딩이나 마진은 내 쪽에서 책임질테니 작업을 할 때 크기를 100%로 해서 작업해주세요'라고 미리 말을 해 놓았으면 나중에 합쳤을 때 조정하는 시간이 덜 걸렸을 것이라는 뒤늦은 깨달음도 얻었다. 이걸 조정하는 데에 엄청난 시간이 걸리거나 대단히 어려운 작업은 아니지만 쓰지 않아도 되는 시간을 할애한 셈이니 결과적으로는 낭비가 아닌가 하는 생각이 들었다.
또 하나 알게 된 것은 position: firxed
는 루트 기준으로 크기를 잡게 되는 것이고 position: absolute
는 가장 가까운 부모 컴포넌트 기준으로 크기를 잡게 되는 것이라고 한다.
import React from 'react';
import styled from '@emotion/styled';
const LoadingIndicator = () => {
return (
<Ring>LOADING
<span></span>
</Ring>
);
};
const Ring = styled.div`
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%,-50%);
width: 15rem;
height: 15rem;
background: transparent;
border: 1rem solid #23a2f7;
border-radius: 50%;
text-align: center;
line-height: 13.5rem;
font-size: 2rem;
font-weight: 1000;
color: #23a2f7;
letter-spacing: 0.3rem;
box-shadow: 0 0 2rem rgba(0,0,0,.5);
text-shadow: 0.2rem 0.2rem 0.2rem lightblue;
:before {
content: '';
position: absolute;
top: -1rem;
left: -1rem;
width: 100%;
height: 100%;
border: 1rem solid transparent;
border-top: 1rem solid #002473;
border-right: 1rem solid #002473;
border-radius: 50%;
animation: animateC 3s linear infinite;
}
span
{
display: block;
position: absolute;
top: calc(50% - 0.2rem);
left: 50%;
width: 50%;
height: 0.4rem;
background: transparent;
transform-origin: left;
animation: animate 3s linear infinite;
}
span:before
{
content: '';
position: absolute;
width: 2rem;
height: 2rem;
border-radius: 50%;
background: #002473;
top: 0rem;
right: -1.4rem;
box-shadow:0 0 2rem #002473;
}
@keyframes animateC
{
0%
{
transform:rotate(0deg);
}
100%
{
transform:rotate(360deg);
}
}
@keyframes animate
{
0%
{
transform:rotate(45deg);
}
100%
{
transform:rotate(405deg);
}
}
`
export default LoadingIndicator;
import React from 'react'
import styled from '@emotion/styled'
interface ITemplate {
children?: React.ReactNode
}
export default function Template({ children }: ITemplate) {
return (
<>
<PageWrapper>{children}</PageWrapper>
</>
)
}
const PageWrapper = styled.div`
margin: 3rem;
display: grid;
grid-template-columns: repeat(2, 1fr);
grid-template-rows: minmax(auto, calc(100vh - 13rem));
height: 100%;
grid-gap: 3rem;
@media (max-width: 768px) {
display: flex;
flex-direction: column;
}
`