본문 바로가기
Development(Web, Server, Cloud)/22) React.js & Node.js

React.js & Node.js 20일차

by tonyhan18 2022. 1. 26.
728x90

이번에는 인스타 내용물을 넣어보자

 

 

이전에 TopNav에 Outlet을 만들었는데 여기에도 주변에 OutletWrapper을 만들어서 출력해주어야 한다.

 

 

이걸 고치기 위해서 box-sizing은 border-box로 전역속성에 넣어줄 필요가 있다. 하는김에 다른 속성들도 모두 넣어주자.

 

import styled from "styled-components";

const PostList = () => {
  return (
    <List>
      <Item>
        <Container>
          <Header>
            <ProfileImage src="" />
            <UserName>user</UserName>
          </Header>
          <Main>
            <PostImage src="" />
            <PostImage src="" />
          </Main>
        </Container>
      </Item>
    </List>
  );
};

const List = styled.ul``;
const Item = styled.li``;
const Container = styled.article`
  background: #fff;
  border: 1px solid #dbdbdb;
  margin-bottom: 24px;
`;
const Header = styled.header`
  display: flex;
  align-items: center;
  padding: 15px;
`;
const ProfileImage = styled.img`
  border-radius: 50%;
  width: 32px;
  height: 32px;
`;
const UserName = styled.span`
  flex: 1px;
  margin-left: 12px;
  color: #262626;
`;
const Main = styled.main``;
const PostImage = styled.img`
  width: 100%;
`;

export default PostList;

 

만들었으면 쿼리를 만들어야 한다.

 

 

일단 아무 이미지나 만들었다.

 

이제 Main에서 데이터를 가져올 수 있도록 만들어보자

 

위와같이 만들고 이제 데이터를 받아와보자

 

routes 폴더 아래에 posts.js 파일을 만들고 안에 위와같이 작성해주자 물론 아직 getPostMain이 없어서 만들어주어야 한다.

 

 

정상적으로 서버에서 데이터가 도착했다. 

 

 

import conn from "../db/index.js";

export const getPostsMain = async (req, res) => {
  const query = `
		SELECT post.*, user.user_name, user.profile_image,
			GROUP_CONCAT(image.url) AS imageList
		FROM post
		JOIN image ON image.post_id = post.id
		JOIN user ON user.id = post.user_id
		GROUP BY post.id
		ORDER BY post.created_at DESC;
		`;
  const [rows] = await conn.query(query);
  const postList = rows.map((post) => {
    const imageList = post.imageList.split(",");
    return { ...post, imageList };
  });
  res.send({ success: true, postList: postList });
};

위와같이 쿼리를 짜서 데이터를 받아오면 배열형태로 데이터가 돌아온다. 그것의 첫번째 데이터를 가져오자

 

결과 위와같이 리스트 형태로 이미지 데이터들이 무더기로 온것을 확인할 수 있다.

 

온 데이터들만 받아서 처리해보자

 

 

이걸 가지고 Main에서 처리해주어야 하는데 이걸 useEffect에서는 async-await를 사용할 수 없다. 그냥 함수를 빼기보다 IIFE를 써보자 IIFE는 즉시 실행함수이기 떄문에 async-await를 사용할 수 있다.

 

받아온 postList를 그냥 보내주면 렌더링이 안되기때문에 useState 안에 넣어서 보내주자.

 

다시금 PostList 에서는 데이터를 받아 화면에 뿌리는 역활을 수행해주자

import styled from "styled-components";

const PostList = ({ data }) => {
  console.log(data);
  return (
    <List>
      {data.map(({ id, content, imageList, profile_image, user_name }) => (
        <Item key={id}>
          <Container>
            <Header>
              <ProfileImage
                src={
                  profile_image === null
                    ? "https://www.luminous-edu.com/wp-content/uploads/2020/06/blank-profile-picture-973460_640.png"
                    : profile_image
                }
              />
              <UserName>{user_name}</UserName>
            </Header>
            <Main>
              {imageList.map((image) => (
                <PostImage key={image} src={image} />
              ))}
            </Main>
          </Container>
          <Content>{content}</Content>
        </Item>
      ))}
    </List>
  );
};

const List = styled.ul``;
const Item = styled.li``;
const Container = styled.article`
  background: #fff;
  border: 1px solid #dbdbdb;
  margin-bottom: 24px;
`;
const Header = styled.header`
  display: flex;
  align-items: center;
  padding: 15px;
`;
const ProfileImage = styled.img`
  border-radius: 50%;
  width: 32px;
  height: 32px;
`;
const UserName = styled.span`
  flex: 1px;
  margin-left: 12px;
  color: #262626;
`;
const Main = styled.main``;
const PostImage = styled.img`
  width: 100%;
`;
const Content = styled.p``;

export default PostList;

 

짜긴했는데 문제점은 한번에 너무 많이 받아온다. 그러니 쿼리문에 제한을 두어서 그 수를 조절하자

 

posts controllers에 LIMIT 15를 두어서 가져오는 데이터의 수를 15개로 제한시켰다.

 

이제 파일을 업로드하기 위한 모달을 만들어보자

 

ModalAddPost를 마지막에 추가할껀데 이건 organisms에 추가할 예정이다.

 

ModalAddPost는 짜피 position:fixed 할것이기 때문에 이렇게 아래에 박아놓아도 괜찮다.

이렇게 먼저 만들고 모달 공통속성으로 atoms에 ModalContainer와 Bakdrop을 빼놓자

 

 

 

 

위와같이 화면이 가린것을 확인할 수 있다.

하지만 내용이 아래로 내려간 느낌이다. 구지 z-index를 넣지 말고 아래와같이 수정해서 모달을 앞으로 나오게끔 만들자

 

 

이제 제대로 나온다.

 

import React, { useState } from "react";
import { Link, Outlet } from "react-router-dom";
import styled from "styled-components";
import {
  IconHome,
  IconActivity,
  IconDirect,
  IconExplore,
  IconNewPost,
} from "../../assets/images/icons/index";
import { ModalAddPost } from "./modals";

function TopNav() {
  // 1. useState이용해서 평소에는 <ModalAddPost /> 안나옴
  // 2. <IconNewPost /> 누르면 <ModalAddPost /> 나오게
  // 3. <ModalAddPost />의 <Backdrop /> 누르면 <ModalAddPost /> 꺼지게
  const [showModalAddPost, setShowModalAddPost] = useState(false);

  return (
    <div>
      <Header>
        <Main>
          <Link to="/">
            <ImgLogo src="https://www.instagram.com/static/images/web/mobile_nav_type_logo-2x.png/1b47f9d0e595.png" />
          </Link>
          <SearchWrapper>
            <InputSearch plceholder="검색" />
          </SearchWrapper>
          <Nav>
            <IconWrapper>
              <IconHome />
            </IconWrapper>
            <IconWrapper>
              <IconDirect />
            </IconWrapper>
            <IconWrapper>
              <IconNewPost onClick={() => setShowModalAddPost(true)} />
            </IconWrapper>
            <IconWrapper>
              <IconExplore />
            </IconWrapper>
            <IconWrapper>
              <IconActivity />
            </IconWrapper>
            <IconWrapper>
              <Link to="/Logout">Logout</Link>
            </IconWrapper>
          </Nav>
        </Main>
      </Header>
      <OutletWrapper>
        <Outlet />
      </OutletWrapper>
      {showModalAddPost && (
        <ModalAddPost onClose={() => setShowModalAddPost(false)} />
      )}
    </div>
  );
}

 

import React from "react";
import styled from "styled-components";
import { ModalContainer, Backdrop } from "../../atoms/modal";

const ModalAddPost = ({ onClose }) => {
  return (
    <>
      <Backdrop onClick={onClose} />
      <Container>
        <Header>새 게시물 만들기</Header>
        <Main>
          <Guide>사진과 동영상을 여기에 끌어다 놓으세요</Guide>
          <BtnFile>컴퓨터에서 선택</BtnFile>
          <InputFile type="file" />
        </Main>
      </Container>
    </>
  );
};

 

하면 버튼 누를때 켜지고 Backdrop 누를떠 꺼진다.

 

문제점은 휠이 남아있어서 스크롤하면 게시글은 넘어간다. 이를 해결하기 위해 TopNav에 다음을 적어주자

 

 

없어진걸 확인할 수 있다.

 

useRef을 이용해서 직접 돔에 접근하자

 

InputFile을 숨기고

 

위와같이 useRef를 이용해서 태그들을 연결해주면 button으로도 input에서 가능했던 파일을 선택하는 기능을 만들 수 있다.

728x90