궁금해 약 [5,6주차 회고]

박정훈·2022년 8월 22일
2

알약 프로젝트

목록 보기
5/7
post-thumbnail

5주차 회고를... 빼 먹었습니다... 우와! 핑계...를 대자면 저번주에 정말 바쁘고 개인적으로 여러 일이 있었어서 프로젝트에 크게 몰입이 안되서... 아무튼 핑계고 다시 으쌰으쌰 해야지..!

5,6주차 개발 노트

  1. my page Profile UI
    grid를 활용해서 공간을 어느 비율로 차지할지 정하는게 편한거 같습니다.

profile

  1. 비밀번호 찾기 ReCAPTCHA 연결
    비밀번호 찾기 page에서도 ReCAPTCHA가 연결 되었습니다.
    React Component인 ReCAPTCHA로 저의 FindPasswordForm 컴포넌트를 감쌉니다.
import * as React from 'react';
declare class ReCAPTCHA extends React.Component<ReCAPTCHAProps> {}
import ReCAPTCHA from "react-google-recaptcha";

function FindPasswordForm() {
  const recaptchaRef = useRef<ReCAPTCHA>(null);
  
    const findPasswordFormik = useFormik({
    initialValues: findPasswordFormInitialValue,
    validationSchema: Yup.object({
      ...(중략)...
    }),
    onSubmit: async (values) => {
      const token = (await recaptchaRef?.current?.executeAsync()) as string;

      const dataToSubmit: AuthEmail = Object.assign(values, {
        token,
      });
      recaptchaRef.current?.reset();

      mutation.mutate(dataToSubmit);
    },
  });
  
  return (
    <ReCAPTCHA
        ref={recaptchaRef}
        size="invisible"
        sitekey={`${process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY}`}>
      
    </ReCAPTCHA>
  )
}
  1. 공통적으로 사용할 컨테이너 공통 컴포넌트로 생성
    _app.tsx 에서는 header와 footer가 조건부로 렌더링 됩니다. 거기에 더해 필요한 경우에 해당 컨테이너를 꺼내다 쓰게 공통 컴포넌트에 추가했습니다.

공통 바디

// common/container/Container
import React from "react";
import {
  FOOTER_HEIGHT,
  FULL_HEIGHT,
  HEADER_HEIGHT,
  MAIN_COLOR,
} from "@utils/constant";
import { Container as OuterContainer, InnerContainer } from "./Container.style";

interface ContainerProp {
  children: React.ReactNode;
}

function Container({ children }: ContainerProp) {
  return (
    <OuterContainer
      $bgColor={MAIN_COLOR}
      $headerHeight={HEADER_HEIGHT}
      $footerHeight={FOOTER_HEIGHT}
      $fullHeight={FULL_HEIGHT}>
      <InnerContainer>{children}</InnerContainer>
    </OuterContainer>
  );
}

export default Container;
  1. 비밀번호 찾기 알림 Modal
    유저에게 비밀번호 찾기가 성공적으로 진행되었는지, 실패했는지 여부를 알려주는 Modal을 띄웁니다.

  2. Capture 컴포넌트 공통 컴포넌트로 분리
    Capture 컴포넌트가 프로필 이미지 변경이나 유저가 알약을 찍을 때 필요한 중복되는 기능이어서 공통으로 뺐습니다.
    이미지는 부모 사이즈에 맞춰 출력됩니다.

import { useState } from "react";
import Image from "next/image";
import { CaptureContainer, CaptureButton } from "./Capture.style";

function Capture() {
  const [source, setSource] = useState("");

  const handleCapture = (event: React.ChangeEvent<HTMLInputElement>) => {
    const { files } = event.target;
    if (files?.length) {
      const file = files[0];
      const newUrl = URL.createObjectURL(file);
      setSource(newUrl);
      // 이미지를 서버에 보내주면 됨!
    }
  };

  return (
    <>
      <Image
        src={source ? source : "/images/register_logo.png"}
        alt="wondering-pill-logo"
        layout="fill"
        objectFit="cover"
        style={{
          borderRadius: "50%",
        }}
        priority={true}
      />
      <CaptureContainer>
        <input
          accept="image/*"
          id="icon-button-file"
          type="file"
          onChange={handleCapture}
          style={{ display: "none" }}
        />
        <CaptureButton htmlFor="icon-button-file" />
      </CaptureContainer>
    </>
  );
}

export default Capture;
  1. myPage 복용약 및 관심 약국 UI 및 react slick 적용
    react-slick으로 관심약국 carousel을 적용 했습니다.

myPage UI

그런데... 이 친구를 적용하면 제 css가... 굴러들어온 slider가 박혀있던 제 css를 부시더군요. slider 이 친구도 난리가 나구요.
styletron의 useStyletron으로 slider에 css를 주입해서 진정 시켰습니다. 또한 slider의 크기만큼 자식의 크기를 조정하고 싶었는데 해결이 안되더군요. slider의 자식들은 vh옵션으로 크기를 조정 했습니다.

<Slider
  {...settings}
  className={css({
    width: "70vw",
    height: "80%",
    maxWidth: "400px",
    margin: "auto",
    border: `1px solid ${MAIN_COLOR}`,
  })}>
  {Object.entries(pharmacyData).map(([key, value], index) => (
    <Pharmarcy
      key={key}
      name={value.name}
      phoneNumber={value.phnoeNumber}
      />
  ))}
</Slider>
  1. 새 비밀번호
    유저가 비밀번호 찾기를 통해서 메일을 받았다면, 리다이렉트 주소가 메일에 함께 전송됩니다. 해당 주소로 타고 들어오면, 새 비밀번호를 입력받는 form이 뜹니다. 해당 페이지는 getServerSideProps를 활용해서 구현되었으며, 이전에는 params로 email이 넘어왔는데 변경되어서 email과 token이 query로 넘어옵니다. (이 또한 백엔드의 요청으로 이후 재 변경됩니다.) 서버로 email과 token을 넘겨주면 유효한 token인지 확인하고 결과를 반환합니다. 결과에 따라 새 비밀번호 page를 렌더링 하던가, error page를 렌더링 하게 됩니다.
interface NewPasswordProp {
  isValidToken: boolean;
}

function NewPassword({ isValidToken }: NewPasswordProp) {
  return (
    <Container>
      <NewPasswordForm isValidToken={isValidToken} />
    </Container>
  );
}

export const getServerSideProps: GetServerSideProps = async (context) => {
  const res = await get<FindPasswordResponse>(
    `/auth/change-password/check?token=${context.query.token}&email=${context.query.email}`,
  );
  const isValidToken = res.result.result;
  return {
    props: {
      isValidToken,
    },
  };

비밀번호 변경에 성공했다면, login page로 email을 query로 넘기고, redirect 시킵니다.

  const mutatuin = useMutation(
    (data: string) =>
      put(`/auth/change-password/${email}`, {
        password: data,
      }),
    {
      onSuccess: () => {
        if (router.query.email) {
          router.push(
            {
              pathname: "/login",
              query: {
                email: router.query.email,
              },
            },
            "/login",
          );
        }
      },
      onError: (error: any) => {
        throw new Error(error);
      },
    },
  );

getServerSideProps를 처음에 page가 아닌 component에 적용을 해서 왜 안돼? 했었습니다. 😢 (꼼꼼히 읽자!)
https://nextjs.org/docs/basic-features/data-fetching/get-server-side-props
getServerSideProps can only be exported from a page. You can’t export it from non-page files.

회고(?)

금방 적용할 줄 알았던 react-slick이 은근히 복병 역할을... 😂
PR도 신경써서 올리다 보니 이전과 같은 실수들은 거의 하지 않게 되었습니다.
또한 생각보다 길어질 수도 있는 일정에 너무 늘어지면 안된다는 생각이 드네요!
그래도 하나하나 기능이 완성 되어 가는 과정을 거치고, 그 과정을 즐기다 보면 그럴싸한 결과물이 나올 것이라 믿고 재밌게 개발..!

profile
그냥 개인적으로 공부한 글들에 불과

2개의 댓글

comment-user-thumbnail
2022년 8월 24일

잘 읽고 갑니다 굿
react-slick css 안 먹히는 부분은 저도 동일하게 고민했던 부분인데 PharmacyWrapper 위에 fragment 하나 추가했었어요 map 돌려야 하다보니 key를 줘야해서 div로 바꾸긴 했는데 이렇게 하면 react-slick이 고정해둔 css는 div태그에 적용되고 그 아래는 자유롭게 css 적용 가능하더라구요

1개의 답글