본문 바로가기
React

2021.09.17 React 로그인, 회원가입(1)

by 해맑은 코린이 2021. 9. 18.

2021.09.17 React 로그인, 회원가입(1)_정리노트

 

아....날ㄹ라가썽.....3시간동안 적은거 다날라갔어....배터리 분명 충전해놨는데....0퍼되서 꺼져서 3시간 날라갔어...너무 참혹하다.......ㅠㅠㅠㅠㅠㅠㅠㅠㅠㅠ 내 맥북이 이제 수명을 다했나봐.... 아 진짜 개 열ㄹ심히적었는데 

결론 ... 임시저장을 제발 생활화 합시다 제ㄹ.......ㅂ ㅏ ㄹ.....

 

 

오늘은 프로젝트 세팅에 이어서 로그인, 회원가입 UI 를 만들고 리덕스를 이용해 상태관리 , 또 백엔드에 만들어 두었던 API 요청까지라 역대급으로 포스팅일ㄹ 길 것 같다.. 왜냐 UI 만들고 리덕스 하는 도중에 꺼졌는데... 그거 포스팅이 3시간 걸렸거든...껄껄ㄹ

 

침착하고.... 조금 전보다는 생략되겠지만 최대한 다시 흐름 복습한다 생각하고 다시해보자! 뺘샤 ㅠ

 

적다보니 너무 많아져서 이거는 최초로..2편으로 나누겠다 후.. 이 포스팅에서는 UI 를 만들고 리덕스 연결해서 상태관리 하는 것까지 하고 끝끝!!!!!

 

 

먼저 UI 로 간다!

 

src/components/auth/AuthForm.js ( 새로 생성 )

앞으로는 계속 스타일 컴포넌트로 스타일링 할 것임! 그리고 컴포넌트들의 최상위도 감싸줄 때 Block 이라는 네이밍 규칙을 써줄텐데, Wrapper, Styled 처럼 자신이 원하는대로 써줘도 된다. 

 

그리고 레이아웃을 보여주는 template  컴포넌트도 작성해줄 예정.

 

src/components/auth/AuthTemplate.js ( 새로 생성 )

 

그런데 이렇게 계속 반복되는 코드가 한번에 짠하고 나타나면 을매나 좋을깡

 

근데 사실 난 이미 책에서 소개한 한 익스텐션을 쓰고 있었는데, 얘를 커스텀화 하면 이거또한 한번에 쳐줄 수 있음. 

 

 

 

얘를 깔아서 쓰고 있었는데, 얘가 하는 일이 뭐냐면

rsc 는 함수형 컴포넌트, rcc 는 클래스형 컴포넌트를 만들어주는 줄임말임 홀홀

 

그니까 얘를 커스텀 해주면 Block 이 들어가게 해서 스타일 컴포넌트와 함께 짠하고 나타낼 수 있음! 

 

https://snippet-generator.app/

 

snippet generator

Snippet generator for Visual Studio Code, Sublime Text and Atom. Enjoy :-)

snippet-generator.app

 

여기로 들어가서 

 

 

좌측 상단에 Description 부분에는 우리가 쓸 user snippet 의 이름을, Tab trigger 부분에는 우리가 rsc 처럼 써줄 줄임말을 쓰고,

${TM_FILENAME_BASE} 는 우리가 적어준 파일의 이름을 들고옴. 그러니까 우리는 파일 이름 + Block 이니께 저렇게 좌측에 Template 코드를 복붙해서 ${TM_FILENAME_BASE} 얘만 바꿔주자 그러고 Copy snippet!

 

 

 

그러고 설정 아이콘에 User Snippet 탭을 들어가서

 

javascriptreact.json 클릭!

 

복사해둔 snippet 붙여넣기!!! 

 

크크 마지막으로 

 

 

 

vs code 하단에 자바스크립트라고 되어 있을거임 얘를 

 

 

Configure File Association for .js 탭에서 

 

 

자바스크립트 리액트로 설정! 그러면 js 코드 앞에 있던 JS 파일 아이콘이 리액트 로고로 바뀌면서 밑에는 자바스크립트 리액트로 설정이 될 것임.

 

그러고 우리가 설정해준 줄임말을 쓰게 되면!!! 

 

 

아이 편하다 ㅎㅎ 매우 유용한 기능인듯.

 


 

 

이제는 AuthTemplate 은 레이아웃을 보여주고 AuthForm을 크게 감쌀거라서 자식 요소들을 렌더링해주세요~ 라고 해줘야한다. 

 

그럴 때 쓰는 것이 children props! 

 

src/componets/auth/AuthTemplate

import React from 'react';
import styled from 'styled-components';

const AuthTemplate = ({children}) => {
  // 부모 컴포넌트 안에 있는 자식 컴포넌트 요소 띄우기
  return (
  <AuthTemplateBlock>
   
    {children}
  
    </AuthTemplateBlock>
    
    )
    ;

};

export default AuthTemplate;

요롷게 넘겨주고

 

src/pages/LoginPage

// login
import React from 'react';
import AuthTemplate from '../components/auth/AuthTemplate';
import AuthForm from '../components/auth/AuthForm';

const LoginPage = () => {
  return(

    <AuthTemplate>
      <AuthForm />
    </AuthTemplate>
  );
};

export default LoginPage;

src/pages/RegisterPage

// register
import React from 'react';
import AuthTemplate from '../components/auth/AuthTemplate';
import AuthForm from '../components/auth/AuthForm';

const RegisterPage = () => {
  return (
    <AuthTemplate>
 
      <AuthForm />
    </AuthTemplate>);
};


export default RegisterPage;

 

이렇게 해서 AuthForm 이라는 글씨가 뜨면 성공! children props 를 넣어주어야 잘 작동한다!

 

이제 레이아웃인 AuthTemplate 을 완성 시키자 ㄱ

 

src/components/auth/AuthTemplate

import React from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
// palette import
import palette from '../../lib/styles/palette';

// 회원가입 / 로그인 페이지의 레이아웃 담당
const AuthTemplateBlock = styled.div`
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  bottom: 0;
  right:0;
  /* 내가 설정해놓은 paltte 에서 2번째 회색을 쓰겠어 */
  background : ${palette.gray[2]};
  display: flex;
  flex-direction:column;
  justify-content: center;
  align-items: center;
`;


// white box

const WhiteBox = styled.div`
  .logo-area {
    display: block;
    padding-bottom: 2rem;
    text-align:center;
    font-weight: bold;
    /* 글자 간격 */
    letter-spacing:2px;
  }
  /* 윤곽선 4면 전부 그림자로 입체감줌 */
  box-shadow: 0 0 8px rgba(0,0,0,0.025);
  padding: 2rem;
  width: 360px;
  background: white;
  border-radius: 2px;
`



const AuthTemplate = ({children}) => {
  // 부모 컴포넌트 안에 있는 자식 컴포넌트 요소 띄우기
  return (
  <AuthTemplateBlock>
    <WhiteBox>
      {/* 전부 스타일 컴포넌트로 만들어주지 않고 가독성을 위해 css selector 사용 */}
      <div className="logo-area">
    <Link to='/'>TITLE</Link>
    </div>
    {children}
    </WhiteBox>
    </AuthTemplateBlock>
    
    )
    ;

};

export default AuthTemplate;

 

 

이렇게 스타일 컴포넌트로 스타일링 해주고, 굳이 전부 스타일 컴포넌트로 만들지 않고, 가독성을 위해 css selector 로 하나 만들어쥼.

 

 

으어... 실행화면 다 날라갔지만,,, 이쁜 하얀색 박스와 함께 AuthForm 글씨가 흰 박스 안에 뜬다면 성공이다 ㅎㅅㅎ

 

이제는 ? 레이아웃을 만들어주었으니, 자식 요소인 form 으로 꼬!

 

src/components/auth/AuthForm

import React from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
// palette import 
import palette from '../../lib/styles/palette';
// Button component import
import Button from '../common/Button';

// 회원가입 or 로그인폼 보여줌.

const AuthFormBlock = styled.div`
    h3{
        margin: 0;
        color:${palette.gray[8]};
        margin-bottom: 1rem;
    }
`;

// button margin
const ButtonWithMarinTop = styled(Button)`
margin-top: 1rem;
`

// styled input

const StyledInput = styled.input`
    font-size:1rem;
    border:none;
    border-bottom: 1px solid ${palette.gray[5]};
    padding-bottom: 0.5rem;
    outline: none;
    width: 100%;
    &:focus{
        color: $oc-teal-7;
        border-bottom:1px solid ${palette.gray[7]} ;
    }
    /* Scss 에서 쓰는 요소가 서로 반복될 때 margin-top 을 줌 >>> input 과 input 사이에 margin-top 줌. */
    &+&{
        margin-top: 1rem;
    }
`
// 폼 하단에 회원가입 링크 style
const Footer = styled.div`
    margin-top:2rem;
    text-align : right;
    a {
        color : 1px solid ${palette.gray[6]};
        text-decoration:underline;
        &:hover{
            color:1px solid ${palette.gray[9]}
        }
    }

`

const textMap = {
    login:'로그인',
    register : '회원가입'
}

const AuthForm = ({type }) => {
    const text = textMap[type];
    return (
        <AuthFormBlock>
            <h3>{text}</h3>
            <form>
                {/* autocomplete 속성 >> 인풋에 자동완성하는 속성 
                 username 은 사용자 이름 , new-password 는 보통 비밀번호 자동완성 막기 위해서 새로운 비밀번호나 비밀번호 확인란에 들어간다는뎅 ㅎ.. 
                 머 일단 책은 이렇고 나는 나중에 보고 current-password 가 더 맞을거 같아서 임의로 바꿨음. */}
                <StyledInput 
                autoComplete="username" 
                name="username" 
                placeholder="아이디"
                
                 />
                <StyledInput 
                autoComplete="current-password" 
                name="password" 
                placeholder="비밀번호" 
                type="password"
                
                
                />
                {/* type 이 회원가입이면, 비밀번호 확인 인풋 추가 */}
                {type === 'register' &&(
                         <StyledInput autoComplete="new-password" name="passwordConfirm" placeholder="비밀번호 확인" type="password" />
                    )
                }
                <ButtonWithMarinTop fullWidth>{text}</ButtonWithMarinTop>
            </form>
            <Footer>
                {type === 'login' ? (
                    <Link to="/register">회원가입</Link>
                ):(
                    <Link to="/login">로그인</Link>
                )
                }
               
            </Footer>
        </AuthFormBlock>
    );
};

export default AuthForm;

 

자 여기서 짚고 넘어가야할 점 정리.

 

먼저 보면 type 이라는 props 를 정해주었다. 우리는 요 하나의 form 컴포넌트를 가지고 로그인 , 회원가입을 띄울 것이기 때문쓰.

 

그래서 

 

const textMap = {
    login:'로그인',
    register : '회원가입'
}

이 부분에서 로그인과 회원가입 타입을 나눠서 text 도 바꿔주고, 회원가입일 때는 비밀번호 확인이라는 인풋을 하나 더 보여주도록 설정해준 것이다. 

 

 {/* type 이 회원가입이면, 비밀번호 확인 인풋 추가 */}
                {type === 'register' &&(
                         <StyledInput autoComplete="new-password" name="passwordConfirm" placeholder="비밀번호 확인" type="password" />
                    )
                }

 

이렇게 회원가입을 할 때는 비밀번호 확인이라는 인풋을 하나 더 띄워주고, 

 

 {type === 'login' ? (
                    <Link to="/register">회원가입</Link>
                ):(
                    <Link to="/login">로그인</Link>
                )
                }

 

마찬가지로 링크 또한 타입에 따라 회원가입, 로그인으로 다르게!!! 

 

 

그러고 나서, 

 

src/pages/LoginPage

// login
import React from 'react';
import AuthTemplate from '../components/auth/AuthTemplate';
import AuthForm from '../components/auth/AuthForm';

const LoginPage = () => {
  return(

    <AuthTemplate>
      
      <AuthForm type='login' />
    </AuthTemplate>
  );
};

export default LoginPage;

 

 

src/pages/RegisterPage

// register
import React from 'react';
import AuthTemplate from '../components/auth/AuthTemplate';
import AuthForm from '../components/auth/AuthForm';

const LoginPage = () => {
  return(

    <AuthTemplate>
      
      <AuthForm type='register' />
    </AuthTemplate>
  );
};

export default LoginPage;

type props 를 Login, RegisterPage 로 넘겨준다.

 

 

실행 화면

http://localhost:3000/login

 

http://localhost:3000/register

 

홀홀 이렇게 떴는감 ~???

 

 

이렇게 뜨면 안됨 ㅎ

 

 

 

 

ㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋㅋ 버튼이 지금 왼쪽에 작게 치우쳐있다면 성공한 것임

 

나는 버튼에 대한 설정을 줬기 때문이다.

 

src/components/common/Button

import React from 'react';
// css import
import styled,{css} from 'styled-components';
import palette from '../../lib/styles/palette';

// button styling
const StyledButton = styled.button`
  border: none;
  border-radius: 4px;
  font-size: 1rem;
  font-weight: bold;
  padding: 0.25rem 1rem;
  color: white;
  outline: none;
  cursor: pointer;
  
  background: ${palette.violet[6]};
  &:hover {
    background: ${palette.violet[4]};
  }
  /* fullWidth props =ture */
  ${props =>
  props.fullWidth &&
  css`
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
  width: 100%;
  font-size: 1.125rem;
  `
  }
   /* fullWidth cyan =ture */
  ${
    props =>
    props.cyan &&
    css`
    background: ${palette.cyan[5]};
    &:hover{
      background: ${palette.cyan[4]};
    }
    `

  }
`;
// Button 에 받아오는 props 를 모두 styledButton 에 전달한다는 의미
const Button = (props) => {
  return <StyledButton {...props} />;
};

export default Button;

 

저번에 세팅할 때 이미 props 를 모두 전달한다고 했으니까 스타일 컴포넌트에서 유용한 기능인 props가 true 이면 해당 스타일이 적용되도록 한것! 이렇게 fullWidth, cyan props 를 설정해줬으면 ?? 

 

 

 

 

src/components/auth/AuthForm

 

요 버튼 컴포넌트에 해당 props 의 이름을 적어주면 true 로 인식해서 width 100% 가 잘 적용이 될것이다! 근데 또 보면 버튼 컴포넌트 이름이 이상하지 않은가 ? ㅎㅅㅎ

 

왜겠엉 내가 직접 다른 컴포넌트로 지어줬으니께 껄꺼

 

// button margin
const ButtonWithMarinTop = styled(Button)`
margin-top: 1rem;
`

 

이렇게 버튼을 직접 가져와서 새로운 컴포넌트 이름으로 정의해주면!!!!

 

 

이렇게 잘뜨면 성공 ㅎ

 

 


 

우왕 그래도 정리해서 적으니까 분량 많이 줄었군 ㅎㅁㅎ 물론 끝이 아니지 겨우 UI 완성했을뿐일라구 후후...ㅠ

 

이제 리덕스로 회원가입/로그인폼 상태 관리해주러 가야함

 

src/modules/auth

import { createAction, handleActions } from 'redux-actions';
// produce import
import produce from 'immer';


// // sample 액션 타입 정의
// // module/ACTION_TYPE. 액션 타입은 대문자로, 앞에 모듈을 붙여주어야 액션 이름 중첩을 방지할 수 있음.
// const SAMPLE_ACTION = 'auth/SAMPLE_ACTION';

const CHANGE_FIELD = 'auth/CHANGE_FIELD';
const INITIALIZE_FORM = 'auth/INITIALIZE_FORM';


// // 액션 생성자. createAction 함수는 매번 객체를 직접 만들어 줄 필요 없이 더욱 간단하게 액션 생성 함수를 선언할 수 있음. 사용으로 액션 추가 데이터는 payload 사용. 
// // 액션 생성자는 export
// export const sampleAction = createAction(SAMPLE_ACTION);

export const changeField = createAction(
    // 추가 데이터 생성은 payload.
    CHANGE_FIELD,
    ({form,key,value}) =>({
        form, // register, login
        key, // username , passowrd, passowrdConfirm
        value // 실제로 바꾸려는 값
    })
);

export const initializeForm = createAction(INITIALIZE_FORM, form => form); //register , login
// 초기 상태 정의
const initialState = {
    register: {
      username: '',
      password: '',
      passwordConfirm: ''
    },
    login: {
      username: '',
      password: ''
    },
}
// 리듀서 함수
// 리듀서 함수도 더 간단하고 가독성 높게 사용하기 위해 handleActions 함수 사용
// 리듀서 함수는 export default 로 
const auth = handleActions(

{
    // 전부 다 추가 데이터가 payload 면 헷갈리니까 객체 비구조화 할당으로 좀 더 직관적으로 사용
    [CHANGE_FIELD]: (state, { payload: { form, key, value } }) =>
      // 불변성 유지를 위해 immer 라이브러리 사용
      produce(state, draft => {
        draft[form][key] = value; // 예: state.register.username을 바꾼다
      }),
    [INITIALIZE_FORM]: (state, { payload: form }) => ({
    // 얘는 오히려 immer 쓰면 복잡해지니까 spread 사용
      ...state,
      [form]: initialState[form],
      
    }),
  },
  initialState,
);


export default auth;

 

역시..리덕스는 어려웡...쿨쩍 다른거는 그려러니하고 넘어가는데 아직 리듀서함수 부분은 공부가 더 필요할 듯하다. 그래도 최대한 흐름 정리하면서 무엇을 쓰고 무엇을 쓰고 정도는..주석으로 달아보았다.

 

구글링과 내 블로그 포스팅인 react-redux 를 보고 참고했는데 내가 이거 너무 책따라 줄줄쳐서 비공개로 해놨네 ㅎ 모르는거는 괜히 올려놓으면 안하느니만 못하니...쨋든 흐름을 이렇게 정리해봐쑴

도움이 되었다면 좋겠지만 쪼다는 여기까지인가보오.. 그래도 주석해놓은 기본 틀이랑 비교하면서 요롷게요롷게 쓰는건강 ?? 하면서 같이 음음 해줬으면 좋겠다 

 

https://velog.io/@jeffyoun/React-Redux-%ED%94%84%EB%A1%9C%EC%84%B8%EC%8A%A4-%EC%A0%95%EB%A6%AC

 

React Redux 프로세스 정리

리덕스를 사용하는 프로세스 정리 리액트를 공부하던 중 리덕스를 사용하기 위해 정형적인 프로젝트 속에서 Action과 State를 사용하는 법을 정리해 놓으면 좋을 것 같아서 정리하는 글임당. 프로

velog.io

 

참고로 나는 이분걸 참고했다. 맨 마지막 부분에 핵심만 정리해놓으신 노트 또한 따로 분리되어 있어서 나는 한번 더 읽기도 좋았따! 나랑 같은 책으로 공부하시는거 같아 내심 기쁨...ㅎ.....ㅋㅋㅋㅋㅋ쨋든 아직 너무 낯설지만 그래도 점점 익숙해지다보면, 나도 리덕스는 이래서 좋으니까 써보고 아니면 안써야지를 판단할 때가 오겠지 ㅎㅅㅎ

 

 

 

이제 컴포넌트에서 리덕스 스토어에 접근해서 상태를 받아오고, 액션을 디스패치해야겠지 ???? 그게 무어였지!?????

컨테이너 컴포넌트...끌끌

비공개 포스팅에서 발그림으로 그렸던 거 캡쳐쓰

 

아주 그냥 책을 줄ㄹ줄 써놨더라 그래서 혼자 근데 줄줄 읽음 ㅎ

쨋든 컨테이너 컴포넌트 만들러 ㄲ

 

src/containers/auth/LoginForm.js ( 새로 생성 )

// useEffect import 
import React,{useEffect} from 'react';
// useSelector, useDispatch import
import { useDispatch, useSelector } from 'react-redux';
// auth module 액션 생성함수 import
import { changeField, initializeForm } from '../modules/auth';
// AuthForm import
import AuthForm from '../components/auth/AuthForm';


const LoginForm = () => {
    const dispatch = useDispatch();
    const { form } = useSelector( ({ auth }) => ( {
        form : auth.login
    }));
    // input change event 로 액션 디스패치. 디스패치 : 액션을 발생시키는 것.
    const onChange = e => {
        const { value, name } = e.target;
        // 액션 생성 함수 import
        dispatch(
            changeField({
                form:'login',
                key:name,
                value
            })
        );
    };
    // form submit event
    const onSubmit = e => {
        e.preventDefault();
        // 구현 예정
    }
    // 컴포넌트 초기 렌더링시 form 초기화
    useEffect(()=>{
        // 액션 생성 함수 import
        dispatch(initializeForm('login'));
    },[dispatch]);

    return (
        
       <AuthForm
       type="login"
       form={form}
       onChange={onChange}
       onSubmit={onSubmit} 
       />
    );
};

export default LoginForm;

 

아웅..길다 여기서 또 정리하자면, 컨테이너 컴포넌트를 만들 때 쓰는 connect 함수를 쓰지 않고 useSelector, useDispath hook 을 썼음

 

useSelector

connect 대신에 리덕스 state를 조회할 수 있다.

 

useDispath

생성한 action 을 발생시키는 hook. 만들어둔 액션 생성함수를 import 하여 사용

 

 



또 디스패치는 form onSubmit, input onChange 이벤트가 발생할 때 되도록 구현했다. 또 useEffect 를 써서 초기 렌더링시 form 초기화를 시키는 작업을 했는데, 얘를 안하게 되면 우리가 페이지를 이동하고 다시 이 페이지로 돌아오게 되었을 때 유저가 썼던 값이 그대로 남아있기 때문 ㅇㅇ!

 

그러고 AuthForm type 과 함께 props를 설정해주었음.

 

후.. 이제 컨테이너 컴포넌트를 만들었다면 

 

src/pages/LoginPage

// login
import React from 'react';
import AuthTemplate from '../components/auth/AuthTemplate';
import AuthForm from '../components/auth/AuthForm';
// LoginForm import
import LoginForm from '../containers/LoginForm';
const LoginPage = () => {
  return(

    <AuthTemplate>
      <LoginForm />
    </AuthTemplate>
  );
};

export default LoginPage;

 

 

AuthForm 대신에 LoginForm 으로 바꿔주자

 

그리고 나서 우리가 컨테이너 컴포넌트에서 props 값을 AuthForm 으로 넘겨줬으니까 AuthForm에서 받을 차례!

 

 

src/components/auth/AuthForm

import React from 'react';
import { Link } from 'react-router-dom';
import styled from 'styled-components';
// palette import 
import palette from '../../lib/styles/palette';
// Button component import
import Button from '../common/Button';

// 회원가입 or 로그인폼 보여줌.

const AuthFormBlock = styled.div`
    h3{
        margin: 0;
        color:${palette.gray[8]};
        margin-bottom: 1rem;
    }
`;

// button margin
const ButtonWithMarinTop = styled(Button)`
margin-top: 1rem;
`

// styled input

const StyledInput = styled.input`
    font-size:1rem;
    border:none;
    border-bottom: 1px solid ${palette.gray[5]};
    padding-bottom: 0.5rem;
    outline: none;
    width: 100%;
    &:focus{
        color: $oc-teal-7;
        border-bottom:1px solid ${palette.gray[7]} ;
    }
    /* Scss 에서 쓰는 요소가 서로 반복될 때 margin-top 을 줌 >>> input 과 input 사이에 margin-top 줌. */
    &+&{
        margin-top: 1rem;
    }
`
// 폼 하단에 회원가입 링크 style
const Footer = styled.div`
    margin-top:2rem;
    text-align : right;
    a {
        color : 1px solid ${palette.gray[6]};
        text-decoration:underline;
        &:hover{
            color:1px solid ${palette.gray[9]}
        }
    }

`

const textMap = {
    login:'로그인',
    register : '회원가입'
}

const AuthForm = ({type , form , onChange, onSubmit }) => {
    const text = textMap[type];
    return (
        <AuthFormBlock>
            <h3>{text}</h3>
            // form onSubmit
            <form onSubmit={onSubmit}>
                // input onChange,value
                <StyledInput 
                autoComplete="username" 
                name="username" 
                placeholder="아이디"
                onChange={onChange}
                value = {form.username}
                 />
                 // input onChange,value
                <StyledInput 
                autoComplete="current-password" 
                name="password" 
                placeholder="비밀번호" 
                type="password"
                onChange={onChange} 
                value ={form.password}
                
                />
                {/* type 이 회원가입이면, 비밀번호 확인 인풋 추가 */}
                {type === 'register' &&(
                // input onChange,value
                        <StyledInput 
                         autoComplete="new-password" 
                         name="passwordConfirm" 
                         placeholder="비밀번호 확인" 
                         type="password" 
                         onChange={onChange}
                         value={form.passwordConfirm}
                        />
                    )
                }
                <ButtonWithMarinTop fullWidth>{text}</ButtonWithMarinTop>
            </form>
            <Footer>
                {type === 'login' ? (
                    <Link to="/register">회원가입</Link>
                ):(
                    <Link to="/login">로그인</Link>
                )
                }
               
            </Footer>
        </AuthFormBlock>
    );
};

export default AuthForm;

 

js 주석으로 일부러 헷갈릴까봐 여기서 작업했더니 똑같이 안보이네 끌끌 코드 복붙이나, 아니면 같이 어디에다가 props 넣어야하는지 보면서 치면 될듯..!

나도 모듈부터 사실 헷갈려서 계속 눈이빠져라봄....

 

실행 화면

 

인풋이 바뀔 때마다 액션이 발생하고, value 값도 잘 들어간당

 

회원가입도 얘처럼 해주면 되겠균 LoginForm 복사해서 RegisterForm 도 만들어주기.

 

 

src/containers/auth/RegisterForm.js ( 새로 생성 )

// useEffect import 
import React,{useEffect} from 'react';
// useSelector, useDispatch import
import { useDispatch, useSelector } from 'react-redux';
// auth module 액션 생성함수 import
import { changeField, initializeForm } from '../modules/auth';
// AuthForm import
import AuthForm from '../components/auth/AuthForm';


const RegisterForm = () => {
    const dispatch = useDispatch();
    const { form } = useSelector( ({ auth }) => ( {
        form : auth.register
    }));
    // input change event 로 액션 디스패치. 디스패치 : 액션을 발생시키는 것.
    const onChange = e => {
        const { value, name } = e.target;
        // 액션 생성 함수 import
        dispatch(
            changeField({
                form:'register',
                key:name,
                value
            })
        );
    };
    // form submit event
    const onSubmit = e => {
        e.preventDefault();
        // 구현 예정
    }
    // 컴포넌트 초기 렌더링시 form 초기화
    useEffect(()=>{
        // 액션 생성 함수 import
        dispatch(initializeForm('register'));
    },[dispatch]);

    return (
        
       <AuthForm
       type="register"
       form={form}
       onChange={onChange}
       onSubmit={onSubmit} 
       />
    );
};

export default RegisterForm;

 

이름 LoginForm >> RegisterForm 으로 변경하고, login 이 적혀있던 부분만 register 로 바꿔주면 됨.

 

src/pages/RegisterPage

// register
import React from 'react';
import AuthTemplate from '../components/auth/AuthTemplate';
import AuthForm from '../components/auth/AuthForm';
// regitserform import
import RegisterForm from '../containers/RegisterForm';
const RegisterPage = () => {
  return (
    <AuthTemplate>
     <RegisterForm />
    </AuthTemplate>);
};


export default RegisterPage;

 

 

듀근듀근 잘 되려나

 

실행 화면

 

이쯤에서 블로그 날라가서 그런가 멘탈이 터져서 몇번이나 사실 고쳤음 ㅎ..... 정신 똑바로 차리고 봐야지...

 


음.... ㅋㅋㅋㅋㅋㅋㅋㅋㅋ 이걸...쓰다보니까 너무 많아서 나누게 됐는데 정말 결국 한 포스팅을 두번 쓴 셈이네 ㅎㅅㅎ ... 젠장....

그래도 UI 쪽 부분 정리안했으면 난리났을거 같애서 긍정...적으로 생각하겠음...추석 전에 거하게 액뗌했다..ㅠ 나는 오늘 새벽에 2편을 쓰기 시작해서...언제 마무리 될 지는 모르겠으나...! 다시 바로 2편으로 오지 그러면 안녕!

 

 

댓글