5 분 소요

Project

팀 프로젝트 Page

React 팀 프로젝트

기능 구현

스피너 구현

카카오 redirect 페이지가 생각보다 오래보여서 안하려고 했던 스피너를 구현하기로 했다.

사실 엄청 간단하게 그냥 원에서 부분적인 색이 돌아가도록 구현할 생각이었는데,

디자이너님이 dot 형태로 디자인해주셔서 꽤 생각을 하거나 라이브러리를 사용하려고 했다.

spinner

이런 형태이다. 저 진한색 방향으로 dot들의 alpha 값이 바뀌면서 돌아가는 형태를 구현하는 스피너이다.

일단 라이브러리를 사용할까 생각했지만, 스피너 종류도 하나이고, 모양이 css로 구현 못할 형태가 아니었기 때문에 라이브러리없이 구현을 해보았다.

처음에 직관적으로 생각해낸 방법은

  1. border dotted

    border dotted 속성을 이용하여 dot을 구현하고, background gradient를 주어 돌아가는 것 처럼 구현하려고 했다.

    하지만, 이 방법은 직사각형일 때는 dot들의 간격이 일정하지만, border-radius를 주는 순간 한 부분의 dot 간격이 붙어버린다.

    게다가 dot 간격을 조정할 수 없기 때문에 다른 방법을 찾아보았다.

  2. 직사각형 div를 만들어 끝에 원

    직사각형 div (background transparent)안에 원을 두어 rotate를 주어서(rotate 기준점도 설정할 수 있으니) 구현하려고 했다.

    하지만, 엄청나게 많은 div (12개 dot이 있으므로 총 24개의 div가 필요..)가 필요했고, 생각해봐도 비효율적일 것으로 생각되어 구현해보지 않았다.

  3. box-shadow

    실제 라이브러리의 코드를 보다가 배운 방법이다.

    라이브러리에서는 div 하나로 사용하고 box-shadow 안에 엄청나게 많은 코드들이 있었다.

    div 한 개는 중심 원이 되고 그것은 보이지 않게 둔다. (transparent 혹은 #fff)

    그래서 box-shadow로 정 위에 그림자를 두기 시작하여 점점점 shadow 위치를 원으로 구현한다. (이 때 약간의 수학이 필요)

    animation을 줄 때에도 100%에서 12개(설정하기 나름)를 나누어서 그 % 마다 색을 이동시킨다고 보면 된다.

    풀어쓰려고 하니 너무 어려운데 코드를 보며 구현해보면 그렇게 어려운 구현은 아니라고 생각된다.

구현 코드

circle {
  width: 7px;
  height: 7px;
  animation: rotate 1s infinite linear;
  border-radius: 50%;
  margin-bottom: 50px;
  box-shadow: 0px -30px 0px 0px var(--main-color), -15px -26px 0 0px rgba(61, 80, 255, 0.93),
    -26px -15px 0 0px rgba(61, 80, 255, 0.86), -30px 0px 0 0px rgba(61, 80, 255, 0.65),
    -26px 15px 0 0px rgba(61, 80, 255, 0.44), -15px 26px 0 0px rgba(61, 80, 255, 0.3),
    0px 30px 0 0px rgba(61, 80, 255, 0.3), 15px 26px 0 0px rgba(61, 80, 255, 0.3),
    26px 15px 0 0px rgba(61, 80, 255, 0.3), 30px 0px 0 0px rgba(61, 80, 255, 0.3),
    26px -15px 0 0px rgba(61, 80, 255, 0.3), 15px -26px 0 0px rgba(61, 80, 255, 0.3);
}

가운데 div(원)에 위와 같이 스타일을 적용한다.

0px -30px은 위, -15px -26px은 왼쪽 위 … 15px -26px은 오른쪽 위 이렇게 된다.

기본적으로 이 원은 30px의 반지름을 갖고 있다고 생각할 수 있다.

그래서 딱딱 떨어지는 30px을 제외하고 나머지 수는 다음과 같이 구할 수 있다.

이미지

원과 원 사이의 각도가 30도 이므로 첫번째 원의 위치는

x: 30 _ cos(60deg), y: 30 _ sin(60deg) 가 된다.

따라서 x = 30 _ 1/2 = 15 이기 때문에 26, y = 15 _ sqrt(3) = 25.98 == 26이다.

x y 값을 box-shadow에 따라 설정해주면 된다.

다음 애니메이션은

@keyframes rotate {
  0%,
  100% {
    box-shadow: 0px -30px 0px 0px var(--main-color), -15px -26px 0 0px rgba(61, 80, 255, 0.93),
      -26px -15px 0 0px rgba(61, 80, 255, 0.86), -30px 0px 0 0px rgba(61, 80, 255, 0.65),
      -26px 15px 0 0px rgba(61, 80, 255, 0.44), -15px 26px 0 0px rgba(61, 80, 255, 0.3),
      0px 30px 0 0px rgba(61, 80, 255, 0.3), 15px 26px 0 0px rgba(61, 80, 255, 0.3),
      26px 15px 0 0px rgba(61, 80, 255, 0.3), 30px 0px 0 0px rgba(61, 80, 255, 0.3),
      26px -15px 0 0px rgba(61, 80, 255, 0.3), 15px -26px 0 0px rgba(61, 80, 255, 0.3);
  }
  8.3% {
    box-shadow: 0px -30px 0px 0px rgba(61, 80, 255, 0.93), -15px -26px 0 0px
        rgba(61, 80, 255, 0.86), -26px -15px 0 0px rgba(61, 80, 255, 0.65), -30px
        0px 0 0px rgba(61, 80, 255, 0.44),
      -26px 15px 0 0px rgba(61, 80, 255, 0.3), -15px 26px 0 0px rgba(61, 80, 255, 0.3),
      0px 30px 0 0px rgba(61, 80, 255, 0.3), 15px 26px 0 0px rgba(61, 80, 255, 0.3),
      26px 15px 0 0px rgba(61, 80, 255, 0.3), 30px 0px 0 0px rgba(61, 80, 255, 0.3),
      26px -15px 0 0px rgba(61, 80, 255, 0.3), 15px -26px 0 0px var(--main-color);
  }
  16.6% {
    box-shadow: 0px -30px 0px 0px rgba(61, 80, 255, 0.86), -15px -26px 0 0px
        rgba(61, 80, 255, 0.65), -26px -15px 0 0px rgba(61, 80, 255, 0.44), -30px
        0px 0 0px rgba(61, 80, 255, 0.3),
      -26px 15px 0 0px rgba(61, 80, 255, 0.3), -15px 26px 0 0px rgba(61, 80, 255, 0.3),
      0px 30px 0 0px rgba(61, 80, 255, 0.3), 15px 26px 0 0px rgba(61, 80, 255, 0.3),
      26px 15px 0 0px rgba(61, 80, 255, 0.3), 30px 0px 0 0px rgba(61, 80, 255, 0.3),
      26px -15px 0 0px var(--main-color), 15px -26px 0 0px rgba(61, 80, 255, 0.93);
  }
  25% {
    box-shadow: 0px -30px 0px 0px rgba(61, 80, 255, 0.65), -15px -26px 0 0px
        rgba(61, 80, 255, 0.44), -26px -15px 0 0px rgba(61, 80, 255, 0.3), -30px
        0px 0 0px rgba(61, 80, 255, 0.3),
      -26px 15px 0 0px rgba(61, 80, 255, 0.3), -15px 26px 0 0px rgba(61, 80, 255, 0.3),
      0px 30px 0 0px rgba(61, 80, 255, 0.3), 15px 26px 0 0px rgba(61, 80, 255, 0.3),
      26px 15px 0 0px rgba(61, 80, 255, 0.3), 30px 0px 0 0px var(--main-color),
      26px -15px 0 0px rgba(61, 80, 255, 0.93), 15px -26px 0 0px rgba(61, 80, 255, 0.86);
  }
  /* 이하 생략 */
}

100% 12로 나누면 약 8.3%가 되어 4등분 0, 25, 50, 75, 100 은 고정하고 나머지는 8.3을 더해가면서 animation을 설정한다.

맨 처음 0,100%일 때에는 정 위가 가장 진한 색이고 그 왼쪽으로 점점 연해진다.

그 다음 8.3일 때는 정 위 원에서 바로 오른쪽 옆 원이 가장 진한 색이 되도록 하고 왼쪽 옆으로 갈 수록 연해지는 색으로 둔다.

이런식으로 11개(0,100은 같으니)를 모두 설정해주면 끝이다.

기타 할일

번역가 가입 폼

  • 입력값 비어있는 것 처리 -> 프론트에서 처리하는 것으로, 필수 사항이 안 갔을 시에만 백엔드에서 에러 메시지 (남) o
  • 지금 career 빈문자열로 넣어주어야 한다. 나중에 career는 지워야함
  • 로그인 로직 처리 (최초 로그인 시 문제) -> 백에 전달 이거 어떻게 하지

번역 의뢰 리스트

  • 견적 기간이 끝난 것들 삭제 안됨 -> 백에서 작업 예정
  • Wrap에 padding bottom 주기 o

견적서 작성 폼

  • 입력값 비어있는 것 처리 -> 프론트 처리 (남) o

내 번역

  • 기능 완료
  • 번역가 쪽에서 확정하기 누르면 갑자기 duplicate됨

견적서 디테일

  • 완료

채팅방 리스트

  • lastChatDate는 항상 0으로 되어 있다. => 백엔드 구현 중
  • isRead도 true true만 뜸 -> 백엔드 구현 중
  • 만약 이것들 기능 구현 안될 시 빼야함
  • 그럼 날짜 대신 정보 같은 거 나타내는 건 어떰???
  • 번역가 프로필 이미지 보여주기 -> 백에서 profileUrl 전달해주어야 함

채팅방

  • 채팅 메시지를 기반으로 이름을 보여주다보니 잘못되어있는 경우 많고, 처음에는 이름이 없음. => client 쪽에서 상담하기 눌렀을 때만 보내주기만 하면 끝 o
  • 확정하기/작업완료 ready 상태에서 안보여지기 하려면 이전 단계에서 status 가져와야 하는데 어떻게 할 것인지
  • 번역가 프로필 이미지 보여주기 -> 채팅방, client쪽에서 상담하기로 넘어올 때 profileUrl 전달받으면 될 듯

마이페이지

  • 세팅 페이지에 회원탈퇴 기능 추가 (추후)

공통

  • mobx 지우기 o
  • 스켈레톤 UI or 스피너 적용 -> 스피너 o
  • 햄버거 메뉴 아이콘 클릭 시 display none 말고 visibility로 줄 수 있는지?? o
  • console 슬슬 지우기
  • error 처리 (trycatch로 바꾸기, error 오는 것 받아서 모달 완성되면 처리)
  • 이미지 파일들 정리
  • 컴포넌트 props 추가된 부분 주석 추가

댓글남기기