반응형

1. next.config.mjs 수정

 

- 위와 같은 package.json 이 있다고 할 때, "npm run build" 를 하면 .next 폴더가 만들어지는데, next.config.mjs파일을 아래와 같이 수정해서 out파일을 배포하도록 설정을 바꿔줘야함.

 - 이미지 등의 경로를 제대로 인식할 수 있도록 basePath를 설정합니다.  [Next.js 의 모든 URL 앞에 추가되는 기본 경로를 설정]

/** @type {import('next').NextConfig} */
const nextConfig = {
  output: 'export',
  basePath: '',
  images: {
    unoptimized: true,
  },
};

export default nextConfig;

 

예전에는 next build 후 out폴더에 static 파일들을 생성하도록 next export 명령어도 했어야 했던 것 같은데, 이번 14버전에서는 next export 대신 config파일에서 output: 'export' 를 설정해주는 것만으로도 작동합니다.

 

.next폴더와 out폴더 두개가 모두 생성되는데, netlify에 배포하는 폴더는 out 폴더입니다. 

 

2. Nelify플러그인 설치

무슨 역할을 하는지는 모르겠지만, 이것도 설치해야한다고 합니다.

npm install @netlify/plugin-nextjs

 

3. netlify.toml 생성

[build]
  command = "npm run build"
  publish = "out"

[[plugins]]
  package = "@netlify/plugin-nextjs"

 

 

이미지는 나왔는데, CSS가 아직 제대로 적용이 안되었네요. 조금 더 검토해봐야겠습니다.

 

오늘은 여기까지!

반응형
반응형

프론트엔드 프레임워크 하면 Angular / React / Vue 그리고 최근 부각되고 있는 Svelte가 있는데요. 전문 개발자는 아니지만 조금은 손대본 경험을 토대로(Angular제외..) 특성을 알아보도록 하겠습니다.

 

Stackoverflow Ranking에서 여러 프로그래밍 언어 및 프레임워크에 대한 점유율, 선호도 등 자세한 정보는 확인하실 수 있습니다. 해당 정보에 따르면 웹 프레임워크의 점유율은 아래 그래프와 같습니다. 이 중에 프론트엔드 프레임워크만 보면 React가 제일 상단에 있고, jQuery, Angular, Vue 그리고 한참 아래쪽에 Svelte가 있네요

 

< 점유율 >

 

역시 Stackoverflow Ranking에 따르면 이번엔 Svelte가 제일 위에 있고, 그다음 React, Vue이고 Angular가 하위에 있는 모습입니다. Svelte가 가장 신생 프레임워크인데, 선호도 측면에서는 많은 개발자들이 만족하고 있다는 의미네요.

 

< 가장 사랑받는 / 피하고싶은>

 

그럼 각 프레임워크의 특징을 한번 살펴보겠습니다.

1. Angular

Angular는 구글에서 만든 Javascript 프레임워크로 초기 1.0버전에서는 AngularJS, 2.0버전부터는 그냥 Angular라고 칭했습니다. 특이한 것은 1.0은 Javascript, 2.0은 Typescript를 기본 언어로 채택하였습니다. Angular는 필요한 요소들을 모두 포함하고 있는 것이 특징입니다. 그만큼 다른 라이브러리들을 추가로 설치할 필요가 없으며, 대신 학습해야할 양이 많은 것으로 알려져 있습니다. 

양방향 데이터바인딩을 지원하며, 태그이름, 템플릿 파일, CSS파일 정보를 decorator문법을 이용하여 전달하는 선언적 코딩 스타일을 사용한다는 특징을 갖고 있습니다. 

 

 

2. React

React는 페이스북에서 만든 라이브러리입니다. 라이브러리냐 프레임워크냐로 갑론을박이 많이 있었지만, 최근에는 라이브러리다라는 의견이 대체적으로 받아들여지고 있는 모습입니다. React는 Virtual-DOM이라는 개념을 적용하였습니다. DOM은 HTML문서를 제어할 수 있는 API 트리 자료구조인데, 실제 DOM을 사용하여 변경시마다 모든 요소를 Update하기에는 무거우며 속도가 느려지는 한계가 있어 가상 DOM이라는 개념을 적용하고 있습니다. 이는 Vue도 마찬가지입니다. 

또다른 특징으로는 Component에 의한 재사용 가능한 UI 생성을 들 수 있으며, 이를 위해 내부적으로 JSX라는 특별한 문법을 사용합니다. 이는 자바스크립트에 HTML을 내포한듯한 모습을 하고 있습니다. 

function App() {
  return (
    <div className="App">
      <header className="App-header">
        <a
          className="App-link"
          href="https://reactjs.org"
          target="_blank"
          rel="noopener noreferrer"
        >
          Hello React
        </a>
      </header>
    </div>
  );
}

Component로 각 요소를 구분해가며 전체 페이지를 구축해가는 방식은 개발자들이 쉽게 접근할 수 있도록 도왔던 것으로 보입니다. Props, State 등의 개념을 익히다가 나중에 상태관리자로 Redux라는 개념을 사용할 때즈음 되면 슬슬 멘붕이 오기 시작합니다. 요즘은 Redux toolkit를 써서 쉽게 접근할 수 있도록 했다고는 하지만, 개념이 바뀌는 것은 아니므로  학습곡선이 완전히 낮아졌다고 하기는 힘들어보입니다.

 

3. Vue

Evan You에 의해 시작된 Frontend Framework로 React와 마찬가지로 Virtual-DOM의 개념을 사용합니다. Evan You는 Angular를 이용한 개발을 하던 중 본인이 필요한 기능들만 추려서 만들었다고 하고, 시기적으로도 React보다 1년 뒤에 나온만큼 Angular, React 등으로부터 영향을 받았을 것으로 보입니다. 단방향 데이터 바인딩만 지원하는 React와는 달리 Vue는 양방향 데이터바인딩을 지원합니다. React를 공부하다보면 Redux 상태관리자를 사용하면서 학습난이도가 올라가는데, Vue에서의 상태관리자인 Vuex는 이보다 훨씬 이해하기 쉬운 장점이 있습니다. 아래는 컴포넌트를 정의하는 샘플 Vue 코드인데, 보시면 template, script, style의 세 부분으로 나뉘어져 있습니다. 이런 구분에 대해서는 개발자마다 호불호가 있지만, 처음 접근하는 입장에서 가독성이 좋은 것은 큰 장점 같습니다.

<template>
    <div class="btn">
        <slot></slot> <!--버튼처럼 텍스트를 표시하기위한 속성-->
    </div>
    <h1 @dblclick="$emit('abc')">
		ABC
    </h1>
</template>
    
<script>
export default{
	emits: [
		'abc'
	]    
}
</script>

<style scoped>
.btn{
    display: inline-block;
    margin: 4px;
    padding: 6px 12px;
    border-radius: 4px;
    background-color: gray;
    color: white;
    cursor: pointer;
}
</style>

 

4. Svelte

기존의 Angular/React/Vue에이어 Svelte는 상당히 나중에 진출한 후발주자입니다. 그런데도 Stackoverflow Ranking에서 보면 개발자가 사랑하는 프레레임워크 2위를 차지할 정도로 관심도가 엄청나며(1위는 Phoenix), 다른 프레임워크들을 추격하고 있습니다. 

The State of JS 2021 Web Frontend Framework

svelte 는 라이브러리/프레임워크가 아닌 컴파일러라고 합니다. Svelte코드를 실행시점에서 해석하지 않고 컴파일 단계에서 Vanilla Javascript 번들로 만들기 때문에 다른 라이브러리를 함께 배포할 필요가 없어집니다. 또다른 특징으로는 React나 Vue처럼  Virtual-DOM을 사용하지 않고 실제 DOM을 직접 제어합니다. 성능적으로 작은 번들사이즈와 DOM의 직접제어 덕분에 다른 프레임워크보다 빠른 반응속도를 보여줍니다. 

문법은 개인적인 느낌이 Vue랑 유사하게 생겼습니다. 가시성도 좋고 쉽다고 생각이 듭니다. 화면 Update할 시점을 인식시켜주기 위한 특이한 용법들도 있습니다만 다른 라이브러리들의 학습곡선에 비할 바가 아니라고 생각됩니다. 상태관리에서도 redux, vuex와 같이 외부 라이브러리를 설치할 필요가 없이 내부에 store를 기본으로 내장하고 있습니다. 

<script>
let name = "teo.yu"
</script>

<div>Hello world! {name}</div>

<style>
div { color: red }
</style>

<번들사이즈 비교>

파일사이즈가 작으면 다운로드속도가 올라가서 체감속도가 빨라집니다. 아래 결과를 보면 Svelte가 가장 작은 사이즈를 보이고 있습니다.

 

이런 성능과 러닝커브 등의 잇점으로 최근 svelte의 인기가 올라가고 있는 것으로 보입니다. 하지만 아직까지 시장의 메인은 React, 그리고 최근에는 Vue도 많이 도입하고 있는 것 같습니다. 저도 Vue를 주로 사용하여 개인 프로젝트들을 만들고 있는데 React에서 어려운 개념들을 익히고 나서 접근해서인지, Vue 너무 좋습니다. 성능도 준수하게 뽑아주는 것 같고, Reference도 잘 되어있어서 당분간은 Vue를 활용할 것 같습니다. Svelte는 조금 더 저변이 넓어지면 그때 적용해볼까 합니다. 

반응형
반응형

1. Library 설치

yarn add swr

 

2. 폴더 구성

Counter는 참고로 작성했는데, 본 포스트에서 구성하지는 않겠습니다. 비동기로 서버에서 User목록만 불러와서 보여주는 부분만 코드로 남김니다.

 

/src

    ㄴcomponents

        *Users.jsx

    ㄴcustomHooks

        *useUsers.js

    *App.js

    *index.js

 

 

 

 

 

 

 

 

 

 

3. 코드

(1) App.js

import Users from "./components/Users";
import Counter from "./components/Counter";

function App() {
  return (
    <>
      <Counter />
      <Users />
    </>
  );
}

export default App;

(2) src/customHooks/useUsers.js

import useSWR from "swr";

export default () => {
  const { data, error } = useSWR(
    "https://jsonplaceholder.typicode.com/users",
    (url) => {
      return fetch(url).then((res) => res.json());
    }
  );
  return { data, error };
};

(3) src/components/Users.jsx

import useSWR from "swr";
import useUsers from "../customHooks/useUsers";

export default function Users() {
  const { data, error } = useUsers();

  return (
    <section>
      <h1>사용자 목록</h1>
      {data && (
        <ul>
          {data.map((user) => {
            return (
              <li key={user.id}>
                {user.username} ({user.email})
              </li>
            );
          })}
        </ul>
      )}
    </section>
  );
}

 

<결과>

반응형
반응형

"리액트를 다루는 기술" 의 22장...mongodb 연동하기 과정에서 Koa 백엔드에 ESM을 적용하는 부분이 있는데, 현재 Node 14 버전에서 주의해야할 점과 함께 몇가지 정리해보고하 합니다. 

 

1. 라이브러리 설치

해당 과정에서는 ESM이 외에도 몇가지 라이브러리가 필요합니다. koa, koa-bodyparser, koa-router, mongoose, dotenv 등 말이죠. ESLint옵션 설정하는 부분은 제외하도록 하겠습니다. (VSCode에 설치한 ESLint 기능에, 추가 설정했을 경우 달라지는 부분을 잘 몰라서 이부분은 제외하고 사용했습니다.) 아래의 package.json을 보면 필요한 라이브러리들을 확인할 수 있을 것입니다. 

{
  "name": "05_react_backend",
  "version": "1.0.0",
  "main": "index.js",
  "license": "MIT",
  "dependencies": {
    "dotenv": "^8.2.0",
    "esm": "^3.2.25",
    "koa": "^2.13.1",
    "koa-bodyparser": "^4.3.0",
    "koa-router": "^10.0.0",
    "mongoose": "^5.12.3"
  },
  "devDependencies": {
    "nodemon": "^2.0.7"
  },
  "scripts": {
    "start": "node src",
    "start:dev": "nodemon --watch src/ src/index.js"
  },
  "type": "module"
}

2. 수정사항

ESM 문법은 2019년10월 Node V12부터 정식 지원되어, 간단한 설정으로 적용이 가능하다고 합니다. 바로, package.json파일에 "type": "module" 부분을 추가하는 것이죠. 교재와 달라진 부분이 index.js에서 ESM관련 설정을 해주고, main.js에 주요 구현을 만들어서 index.js에서 불러오게끔 했는데, 이젠 그럴필요 없이 package.js에서 저부분만 추가해주면 됩니다. 

 

3. 오류  

 이것만 하면 금방 적용될 줄 알았는데, 오류가 납니다. 무슨...ESM 오류라고하는데... 알고보니 start할때 책에 써있는

"-r esm" 부분이 필요가 없습니다. 지워줍니다. (위 package.json 참조)

 

 그런데 또 오류가 납니다. 여전히 ES Module에서 지원하지 않는 구문이라고 합니다. 알아보니, Node V14부터는 ESM을 적용할 때 파일 확장자까지 정확하게 써줘야 한다고 합니다. 하위 파일들도 마찬가지로요.

 

 

 

 

 

마지막으로 .env 파일과 관련되어 수정할 부분이 있습니다. 책에서는 

require('dotenv').config();

와 같이 사용하는데 이걸 

import dotenv from 'dotenv';
dotenv.config();

와 같이 수정해줘야 합니다.

 

 

그럼 모두 건승하세요~~

 

~~끝~~

반응형
반응형

비주얼 테스트 도구인 스토리북에 대해 알아보겠습니다. 

 

1. 설치

npx -p @storybook/cli sb init

.storybook폴더가 자동 생성되고, 그 안에 story파일을 로딩해주는 main.js 와 preview.js 파일이 있습니다. main.js에는 ...story.js파일을 자동 추가/로딩해주는 부분과 addon 설정부분이 있습니다. (preview.js는 아직 잘 모르겠습니다.ㅠㅠ)

package.json에도 storybook 스크립트가 추가되었습니다. (모두 이전 버전에서 수동 생성해주던 부분임...)

 

2. Sample 컴포넌트 작성 <./src/components/Input.jsx 파일 생성>

기존 컴포넌트들이 있으면 활용해도 되지만, 저는 새로 만든 프로젝트에서 테스트할 용도로 간단한? 컴포넌트 하나를 만들겠습니다.

import React, { PureComponent } from 'react';
import PropTypes from 'prop-types';

class Input extends PureComponent {
    constructor(props) {
        super(props);
        this.setRef = this.setRef.bind(this);
        this.handleChange = this.handleChange.bind(this);
    }
    handleChange(e) {
        const { name, onChange } = this.props;
        if(onChange) {
            onChange(name, e.target.value);
        }
    }
    componentDidMount(){
        if(this.props.autoFocus){
            this.ref.focus();
        }
    }
    componentDidUpdate(){
        if(this.props.autoFocus) {
            this.ref.focus();
        }
    }
    setRef(ref){
        this.ref = ref;
    }
    render() {
        const { errorMessage, label, name, value, type, onFocus } =this.props;
        return (
            <label>
                {label}
                <input
                  id={`input_${name}`}
                  ref={this.setRef}
                  onChange={this.handleChange}
                  onFocus={onFocus}
                  value={value}
                  type={type}
                />
                {errorMessage && <span className="error">{errorMessage}</span>}                
            </label>
        );
    }
}

Input.propTypes = {
    type: PropTypes.oneOf(['text','number','price']),
    name: PropTypes.string.isRequired,
    value: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
    errorMessage: PropTypes.string,
    label: PropTypes.string,
    onChange: PropTypes.func,
    onFocus: PropTypes.func,
    autoFocus: PropTypes.bool,
};
Input.defaultProps={
    onChange: () => {},
    onFocus: () => {},
    autoFocus: false,
    type: 'text',
};

export default Input;

 

3. 스토리 파일 생성(./src/stories/Input.stories.js)

위에서 만든 컴포넌트를 스토리북으로 넘겨줄 story파일을 만들겠습니다.

import React from 'react';
import { storiesOf } from '@storybook/react';
import Input from '../components/Input';

storiesOf('Input', module).add('기본설정', () => <Input />);

src/components/Input.jsx 와 src/stories/Input.stories.js 파일 구조. 선호하는 방식에 따라 변경 가능하다.

4. 실행 및 확인

yarn storybook

 

샘플 컴포넌트들이 하단에 나오고, 새로 생성한 Input 컴포넌트가 가장 위에 표시되는 모습

 


 

스토리북이 버전이 올라가면서 설정할 것도 별로 없어지고 편리해졌네요. story파일 자동으로 추가되도록 스크립트도 이미 생성이 되어있고, storybook 명령도 package.json파일에 자동 등록되고, 무엇보다도 개별 story를 등록해줄 필요도 없어진 것 같아 훨씬 편하게 사용할 수 있게 되었네요. 그냥 설치하고 xxx.stories.js 파일만 생성해주면 자동 등록되네요. 예전 책 "리액트 프로그래밍 정석" 으로 따라하다 잘 안되서 최신 사용법을 찾아본 소감이었습니다.

 

~끝~

반응형
반응형

1. Build

"create-react-app ." 명령으로 react app 생성 후 배포를 위해서는 아래의 명령으로 buid 합니다.

npm run build

이렇게 하면 build 폴더가 생성되는데 필요한 파일을 서버가 작동하는 폴더에 위치시키면 됩니다.

 

2. httpd.conf 항목 추가

매번 build된 파일을 apache가 서비스하는 폴더로 옮기는 작업은 번거롭습니다. 현재 작업중인 폴더를 직접 서비스하도록 추가하고 싶다면, httpd.conf파일에 아래의 항목을 추가해줍니다.

Alias /react "D:/05_Program_dev/03_web/04_react_hello/build"

<Directory "D:/05_Program_dev/03_web/04_react_hello/build">
    Options Indexes FollowSymLinks
    AllowOverride None
    Require all granted
</Directory>

이렇게 하면 'localhost/react'로 접속 시도시 해당 폴더에 접속하게 됩니다. 따라서 작업한 파일을 보려면

'localhost/react/index.html'로 시도하면 됩니다. 

 

3. package.json homepage 수정

아직 apache 설정이 익숙치 않아서 이것저것 건드려봅니다. 어떤 앱은 2번까지만 수행해도 되고, 또다른 경우에는 여전히 흰 화면만 나오고 있습니다. 이것이 정답인지 알기는 힘들지만, 이럴땐 homepage속성에 접근하고자 하는 url을 입력했더니 수행 가능해졌습니다.

4. .htaccess 파일 추가??

.htaccess 파일을 생성하는 방법이 있습니다. 배포 폴더 내에 또는 Build하기 전 /publish 폴더 내에 .htaccess 파일을 추가하여 아래와 같이 작성해줍니다. 

<.htaccess>

Options -MultiViews 
RewriteEngine On 
RewriteCond %{REQUEST_FILENAME} !-f 
RewriteRule ^ index.html [QSA,L]

이 방법을 쓰려면 httpd.conf 파일의 AllowOverride 부분을 AllowOverride All로 해야합니다. 그래야 .htaccess의 접근을 허용해줍니다. 그러나 이 방법 성능 및 보안의 문제가 있다고 하여 사용을 권장하지는 않는 것 같습니다. (참고자료)

 

또..어떤 분은 아래와 같이 속성을 지정한다고 했는데 이건 저도 안해봤습니다. 이래저래 안될경우 따라해보려고 소스만 남겨놓습니다. 상황에 맞게 시도해보시기 바랍니다.

FallbackResource ./index.html

 

반응형
반응형

오늘은 React에서 Highchart 사용하여 아래와 같은 BAR 차트를 한번 그려보려고 합니다. 

1. 설치

우선 create-react-app으로 리액트 환경을 구축하고, Highcharts 와 highcharts-react-official을 설치해 줍니다.

create-react-app .	// '.' 을 입력하면 현재 폴더에 리액트를 설치합니다.
npm install highcharts --save
npm install highcharts-react-official --save

 

2. High.js (Highchart 컴포넌트) 작성

이번 예제에서는 App.js에서 데이터를 props로 전달해주고, Highchart 컴포넌트에서 이를 받아 그래프를 그리도록 구성하겠습니다. 따라서 그래프를 그리는 High.js 를 작성합니다. 

import React, { Component, Fragment } from "react";
import Highcharts from "highcharts";
import HighchartsReact from "highcharts-react-official";

class High extends Component {
    render() {
        const series2 = this.props.data;    //App.js에서 데이터를 보내줄 예정
        const options = {
            chart: {
                type: 'bar'		// bar차트. 아무 설정이 없으면 line chart가 된다.
            },
            title: {
                text: 'My first bar chart'
            },
            credits: {
                enabled: false
            },
            xAxis: {
                type: 'category'
            },
            legend: {
                reversed: true
            },
            plotOptions: {
                series: {
                    stacking: 'normal',
                    dataLabels: {
                        enabled: true,
                        format: "<b>{point.y}</b>",
                    }
                }
            },
            series: [{ name: "data", data: series2 }]

        }
        return (
            <Fragment>
                <HighchartsReact highcharts={Highcharts} options={options} />
            </Fragment>
        );
    }
}
export default High;

 

3. App.js 수정

App.js에서는 위에서 작성한 High.js를 임포트해주고, data를 state로 선언해준 다음 <Highcharts>의 props로 전달해줍니다.

import React, { useState, useEffect, Component } from 'react';
import HighCharts from './High';
import './App.css';


class App extends Component {
  constructor(props) {
    super(props);
    this.state = {
      data: [40000, 50000, 60000, 70000, 80000, 100000, 120000, 150000]
    }
  }
  render() {
    return (
      <div className="App">
        <HighCharts
          data={this.state.data} />
      </div>
    );
  }
}

export default App;

 

실행하면 맨 윗화면처럼 나오게 될 겁니다. 

잘 되시길....행운을 빕니다.

 

(ps) option - type 값을 변경하여 여러가지 차트를 그릴 수 있습니다.

1. type: 'line'

2. type: 'column'

3. type: 'areaspline'

4. type: 'pie'

5. type:'scatter'

- 끝 -

반응형
반응형

1. App.js

import React from 'react';
import Contact from './Contact';
import './App.css';

function App() {
  return (
    <Contact />
  );
}

export default App;

2. Contact.js

import React, { Component } from 'react';
import ContactInfo from './ContactInfo';

class Contact extends Component {
    constructor(props) {
        super(props);
        this.state = {
            selectedKey: -1,
            keyword: '',
            contactData: [{
                name: 'Abet',
                phone: '010-000-0001'
            }, {
                name: 'Betty',
                phone: '010-000-0002'
            }, {
                name: 'Charlie',
                phone: '010-000-0003'
            }, {
                name: 'David',
                phone: '010-000-0004'
            }]
        }

    }

    render() {

        const mapToComponents = (data) => {
            return data.map((contact, i) => {
                return (<ContactInfo contact={contact} key={i} />);
            })
        }
        return (
            <div>
                <h1>Contacts</h1>
                <div>{mapToComponents(this.state.contactData)}</div>
            </div>
        );
    }
}

export default Contact;

 

3. ContactInfo.js

import React, { Component } from 'react';

class ContactInfo extends Component {
    render() {
        return (
            <div>{this.props.contact.name}{this.props.contact.phone}</div>
        );
    }
}

export default ContactInfo;
반응형

+ Recent posts