[클론코딩] velog 클론코딩

AnSuebin·2023년 1월 1일
1

velog 클론코딩 깃허브

🏁 01. 프로젝트 시작

항해 99 리엑트 2주차 과제는 백과 협업하여, 프로젝트를 만들기였습니다. 한번은 도전 하고 싶었던 개발 블로그라는 주제에, 적은 시간에 높은 완성도를 낼 수 있는 클론 코딩을 결합하여, velog 클론 코딩을 기획하여 1주일동안 프로젝트를 진행하였습니다.

🏁 02. 프로젝트 기간 및 맡은 역할

  • 개발 기간 : 2022.12.16 ~ 2022.12.21 (약 1주)
  • 맡은 역할 : 로그인, 회원가입, 작성 및 수정 페이지, 게시물 상세 페이지

🏁 03. 기술 스택

  • 프론트 : react
  • 백 : spring

🏁 04. API 설계

📓 05. 페이지별 소개

01. 회원가입, 로그인

  • 정규식을 통한 1차 조건 체크
  • 아이디, 닉네임 중복 체크
  • 토큰 localStorage 저장
회원가입 페이지로그인 페이지

02. 메인 페이지

  • 마크다운 형태로 요약 보여주는 기능
  • 태그별로 조회 기능

03. 작성, 수정 페이지

  • 태그 작성 란에서 엔터 시, 태그로 변환되는 기능, 클릭 시 삭제 기능
  • 작성 시, 마크다운 언어를 미리 볼 수 있는 기능

04. 게시글 확인 페이지, 댓글 기능

  • 마크다운으로 작성 한 글 변환 형태
  • 댓글 작성, 삭제, 수정 기능

🔥 06. 구현 포인트

  • 회원가입 조건 체크
    • 정규식을 활용하여, 회원가입 규칙 경고를 적용하였습니다.
          <input
            name="loginId"
            value={form.loginId}
            autoComplete="username"
            id="loginId"
            onChange={onChangeInputHandler}
            ref={inputLoginId}
            placeholder="영문 소문자, 숫자가 모두 포함된 4~12자리로 작성해주세요."
          ></input>
          <Button
            margin="0 0 0 15px"
            fontFamily="bold"
            onClick={onClickCheckUsername}
          >
            중복체크
          </Button>
        </div>
        {/* 아이디 규칙 적용 경고*/}
        {!/^(?=.*\d)(?=.*[a-z])[0-9a-z]{4,12}$/.test(form.loginId) &&
          form.loginId && (
            <p>영문 소문자, 숫자가 모두 포함된 4~12자리로 작성해주세요.</p>
          )}

        <label htmlFor="password">비밀번호</label>
        <input
          type="password"
          name="password"
          autoComplete="new-password"
          value={form.password}
          id="password"
          onChange={onChangeInputHandler}
          placeholder="영문, 숫자, 특수문자가 모두 포함된 8~16자리로 작성해주세요."
        ></input>
        {/* 비밀번호 규칙 적용 경고 수정 필요*/}
        {!/^(?=.*[a-zA-z])(?=.*[0-9])(?=.*[$`~!@$!%*#^?&\\(\\)\-_=+]).{8,16}$/.test(
          form.password
        ) &&
          form.password && (
            <p>영문, 숫자, 특수문자가 모두 포함된 8~16자리로 작성해주세요.</p>
          )}
  • 마크다운 적용
    • react-markdown이라는 라이브러리를 활용하여, 마크다운 작성 활성화
    • 라이브러리만으로는 완전한 마크다운을 제작하기 어렵고, 다른 것들을 지정해줘야 했기 때문에, 다음에 제작하게 된다면 다른 라이브러리도 시도할 예정
import React from "react";
import styled from "styled-components";
import ReactMarkdown from "react-markdown";
// -- 중간줄 구현하기
import remarkGfm from "remark-gfm";
// code 보여주기
import { Prism as SyntaxHighlighter } from "react-syntax-highlighter";

function BlockQutoe(children) {
  return (
    <BlockQutoeStyle>
      <span>{children.children}</span>
    </BlockQutoeStyle>
  );
}

const MarkdownRender = ({
  markdown,
  height,
  fontsize,
  margin,
  overflow,
  cursor,
  onClick,
  color,
}) => {
  return (
    <MarkDownStyle
      fontsize={fontsize}
      color={color}
      margin={margin}
      overflow={overflow}
      cursor={cursor}
      onClick={onClick}
    >
      <TableContainer height={height}>
        <ReactMarkdown
          remarkPlugins={[remarkGfm]}
          children={markdown}
          components={{
            blockquote: BlockQutoe,
            code({ node, inline, className, children, ...props }) {
              const match = /language-(\w+)/.exec(className || "");
              return !inline && match ? (
                <SyntaxHighlighter
                  children={String(children).replace(/\n$/, "")}
                  language={match[1]}
                  {...props}
                />
              ) : (
                <code className={className} {...props}>
                  {children}
                </code>
              );
            },
          }}
        ></ReactMarkdown>
      </TableContainer>
    </MarkDownStyle>
  );
};

const MarkDownStyle = styled.div`
  font-size: ${({ fontsize }) => fontsize || "16px"};
  line-height: 2.5rem;
  overflow-y: ${({ overflow }) => overflow || "auto"};
  margin: ${({ margin }) => margin || "0"};
  color: ${({ color }) => color || "gray"};
  cursor: ${({ cursor }) => cursor || "default"};
`;

// 표일 때
const TableContainer = styled.div`
  height: ${({ height }) => height || "200px"};
  width: 100%;
  img {
    width: 50%;
    height: 100%;
    object-fit: cover;
  }
  table {
    width: 100%;
    border-collapse: collapse;
  }
  th,
  td {
    border: 1px solid #ccc;
    padding: 5px;
  }
`;

const BlockQutoeStyle = styled.blockquote`
  width: 97%;
  padding: 0 0.8rem;
  border-left: 5px solid var(--color-deep-red);
  margin-left: 0;
  background-color: var(--color-light-gray);
  color: white;
  span {
    color: white;
    background-color: transparent;
  }
`;

export default MarkdownRender;
  • 작성 시, 카테고리 엔터 키로 입력 기능
    • category input의 state값과, category list의 state값을 따로 만들어 엔터를 치면, input의 내용이 list로 푸쉬되고, input은 초기화 되도록 작성
    • 클릭시, 테그의 innerText를 활용하여 filter로 해당 value를 걸러내도록 구현
  // 테그 인풋 state 가져오기
  const onChangeTagHandler = (event) => {
    event.preventDefault();
    const { name, value } = event.target;
    dispatch(
      changeField({
        form: "editPost",
        key: name,
        value,
      })
    );
  };

  //-- 엔터칠 때 태그 리스트로 들어가게 하기 --//
 const onKeyUp = (event) => {
    if (event.target.value.length !== 0 && event.key === "Enter") {
      submitTagItem();
    }
  };

  const submitTagItem = () => {
    let updateTagList = [...form.tags];
    updateTagList.push(form.tag);
    dispatch(
      changeField({
        form: "editPost",
        key: "tags",
        value: updateTagList,
      })
    );
    dispatch(
      changeField({
        form: "editPost",
        key: "tag",
        value: "",
      })
    );
  };

  const deleteTagItem = (event) => {
    const deleteTagItem = event.target.innerText;
    const filteredTagList = form.tags.filter(
      (tagItem) => tagItem !== deleteTagItem
    );
    dispatch(
      changeField({
        form: "editPost",
        key: "tags",
        value: filteredTagList,
      })
    );

🏁 07. 마치며

백과 함께 협업하게 되어 무척 즐거웠습니다. 협업 과정에서 다양하게 시도할 수 있어서 즐거웠고, 시각적으로 눈에 띄는 기술들을 적용해 볼 수 있어서 더 재밌고 완성도 높다고 느껴지는 프로젝트였습니다.

profile
고객에게 명료한 의미를 전달하고, 명료한 코드를 통해 생산성 향상에 기여하고자 노력합니다.

0개의 댓글