Skip to content

[재하] 1205(화) 개발기록

박재하 edited this page Dec 5, 2023 · 3 revisions

목표

어드민 페이지 Full Stack, 전체 글 조회기능 구현

  • emotion 설치
  • Button, Table 컴포넌트 생성
  • 전체 글 조회 API 구현
  • Board 컴포넌트 생성, 전체 글 조회
  • 트러블 슈팅: Vite dev/prod 환경변수 설정
  • 테스트 (동작 화면)

emotion 설치

yarn workspace admin add @emotion/styled @emotion/react

프론트 프로젝트와 의존성을 동일하게 가기 위해 @emotion/styled, @emotion/react를 설치해준다.

Button, Table 컴포넌트 생성

버튼, 테이블 등 공유해서 사용할 수 있는 베이스 컴포넌트들을 생성해준다. 학습메모 2를 참고하여 프론트와 동일한 코드 컨벤션으로 구현을 해보았다.

// Button.tsx
import styled from '@emotion/styled';

interface PropsType extends React.ButtonHTMLAttributes<HTMLButtonElement> {
	onClick?: () => void;
	children: React.ReactNode;
}

export default function Button({ children, ...args }: PropsType) {
	return <CustomButton {...args}>{children}</CustomButton>;
}

const CustomButton = styled.button<PropsType>`
	background-color: #fff;
	border: 1px solid #ddd;
	border-radius: 4px;
	color: #212121;
	font-size: 14px;
	font-weight: 600;
	padding: 6px 12px;
	cursor: pointer;
	outline: none;
	&:hover {
		background-color: #ddd;
	}
`;
// Table.tsx
import styled from '@emotion/styled';

interface PropsType extends React.TableHTMLAttributes<HTMLTableElement> {
	children: React.ReactNode;
}

export default function Table({ children, ...args }: PropsType) {
	return <CustomTable {...args}>{children}</CustomTable>;
}

const CustomTable = styled.table<PropsType>`
	border-collapse: collapse;
	width: 100%;
	border: 1px solid #ddd;
`;

스타일은 추후에 또 적당히 수정해보자.

전체 글 조회 API 구현

백엔드에 전체 글 조회 API가 없기 때문에 만들어둔 admin 모듈에 새로운 API를 만들어준다.

// admin.controller.ts
import { Controller, Get } from '@nestjs/common';
import { AdminService } from './admin.service';

@Controller('admin')
export class AdminController {
	constructor(private readonly adminService: AdminService) {}

	@Get('post')
	getAllPosts() {
		return this.adminService.getAllPosts();
	}
}
// admin.service.ts
import { Injectable } from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { User } from 'src/auth/entities/user.entity';
import { Board } from 'src/board/entities/board.entity';
import { Repository } from 'typeorm';

@Injectable()
export class AdminService {
	constructor(
		@InjectRepository(User)
		private readonly userRepository: Repository<User>,
		@InjectRepository(Board)
		private readonly boardRepository: Repository<Board>,
	) {}

	async getAllPosts() {
		const posts = await this.boardRepository.find();
		return posts;
	}
}

Board 컴포넌트 생성, 전체 글 조회

board 메뉴에 보여줄 페이지를 컴포넌트로 만들어준다. 마찬가지로 학습메모 5, 프론트 분들의 코드를 참고하여 동일한 컨벤션으로 구현 시도.

import { useState } from 'react';
import Button from '../shared/Button';
import Table from '../shared/Table';

const baseUrl = import.meta.env.VITE_API_BASE_URL;

export default function Board() {
	const [boardList, setBoardList] = useState([]);

	const getBoardList = async () => {
		const response = await fetch(baseUrl + '/admin/post');
		const data = await response.json();
		setBoardList(data);
	};

	return (
		<div>
			<Button onClick={getBoardList}>게시글 불러오기</Button>
			<Table>
				<thead>
					<tr>
						<th>번호</th>
						<th>제목</th>
						<th>작성자</th>
						<th>좋아요</th>
						<th>이미지 </th>
						<th>작성일시</th>
						<th>수정일시</th>
					</tr>
				</thead>
				<tbody>
					{boardList.map((board: any) => (
						<tr key={board.id}>
							<td>{board.id}</td>
							<td>{board.title}</td>
							<td>{board.user.nickname}</td>
							<td>{board.like_cnt}</td>
							<td>{board.images.length}</td>
							<td>{board.created_at}</td>
							<td>{board.updated_at}</td>
						</tr>
					))}
				</tbody>
			</Table>
		</div>
	);
}

Nav바 리팩토링

완성된 페이지는 라우트와 Nav바에 등록해줘야 한다.

Nav바는 아래와 같이 리팩토링해서 App.tsx에서 메뉴를 한번에 편집이 가능하도록 변경했다.

// Nav.tsx
import styled from '@emotion/styled';

interface PropsType extends React.HTMLAttributes<HTMLDivElement> {
	children: React.ReactNode;
}

export default function Nav({ children, ...args }: PropsType) {
	return <CustomNav {...args}>{children}</CustomNav>;
}

// 최상단에 위치하도록 설정
const CustomNav = styled.nav<PropsType>`
	position: fixed;
	top: 0;
	left: 0;
	width: 100%;
	height: 20px;

	display: flex;
	align-items: center;
	justify-content: space-around;
	background-color: #fff;
	border-bottom: 1px solid #ddd;
	padding: 12px 12px;
	& > a {
		margin-right: 16px;
		text-decoration: none;
		color: #212121;
		font-size: 14px;
		font-weight: 600;
	}
	& > a:hover {
		text-decoration: underline;
	}
`;

App.tsx에서 라우트와 Nav바에 Board 등록

이제 최종적으로 board 페이지 등록.

// App.tsx
import { Route, Routes } from 'react-router-dom';
import './App.css';
import { TestComponent } from './components/TestComponent/TestComponent.tsx';

import Board from './components/Board/Board.tsx';
import Nav from './components/Nav.tsx';

function App() {
	return (
		<>
			<Nav>
				<a href="/">Home</a>
				<a href="/about">About</a>
				<a href="/abc">Test</a>
				<a href="/board">Board</a>
			</Nav>
			<Routes>
				<Route path="/" element={<div>Home</div>} />
				<Route path="/about" element={<div>About</div>} />
				<Route path="/abc" element={<TestComponent />} />
				<Route path="/board" element={<Board />} />
			</Routes>
		</>
	);
}

export default App;

트러블 슈팅: Vite dev/prod 환경변수 설정

board 페이지에서 게시글 전체 조회를 위한 fetch 시 baseUrl을 개발 환경에서는 http://localhost:3000, 배포 환경에서는 https://www.별글.site/api로 해줘야 하는데,

기존에 알고 있던 REACT 환경변수가 먹히지 않아 애를 좀 먹었다. 결국 본 프로젝트에서는 Vite를 이용하여 dev서버 실행과 build를 진행하므로, Vite에서 제공하는 환경변수 import 형태를 준수해야 했다.

학습메모 4를 참고하여 admin workspace 루트인 /packages/admin/.env.development, .env.production 두 파일을 추가해준다.

VITE_API_BASE_URL=http://localhost:3000
VITE_API_BASE_URL=https://www.별글.site/api

불러올 땐 아래와 같이 불러오면 된다.

const baseUrl = import.meta.env.VITE_API_BASE_URL;

테스트 (동작 화면)

yarn workspace admin dev
스크린샷 2023-12-05 오후 1 57 41

이제 dev서버에서 요청하면 http://localhost:3000로 잘 가고

yarn workspace admin build
스크린샷 2023-12-05 오후 1 10 06

빌드해보면 https://www.별글.site/api이 잘 등록되어 있음을 확인할 수 있다.

스크린샷 2023-12-05 오후 1 25 53

전체적인 실행 화면은 위와 같다.

학습메모

  1. [React] emotion과 styled-component의 차이
  2. button 컴포넌트 예시
  3. REACT env 사용
  4. vite env 사용
  5. 페이지 컴포넌트 예시

소개

규칙

학습 기록

[공통] 개발 기록

[재하] 개발 기록

[준섭] 개발 기록

회의록

스크럼 기록

팀 회고

개인 회고

멘토링 일지

Clone this wiki locally