NextJS 커스텀 서버와 테스트

bicco2·2023년 3월 26일
0

Chapter.8 커스텀 서버

커스텀 서버를 사용하는 경우

Nextjs는 웹 서버 따로 설정하지 않아도 서버 측 렌더링 웹 앱을 쉽게 만들 수 있음 .

대부분의 경우 필요없지만 express나 fastify 등 별도의 서버가 필요한 경우도 있다.

  • Nextjs로 리펙토링
    리펙토링을 nextjs로 진행해 기존 로직이나 미들웨어를 유지하고 싶은 경우

  • 멀티테넌시 지원이 필요한 경우
    멀티 도메인 (현재 호스트명에 따라 선택적으로 렌더링하는 기능)이 있지만 도메인이 많거나 워크 플로를 단순화 해야하는 경우

  • 세부 제어가 필요하는 경우


Express.js 서버

const { parse } = require('url');
const express = require('express');
const next = require('next');

const dev = process.env.NODE_ENV !== 'production';
const app = next({ dev });

async function main() {
  try {
    await app.prepare();

	  // next 대신 요청 처리
    const handle = app.getRequestHandler();

    const server = express();

    server
      .get('/', (req, res) => {
        res.send('Hello World!');
      })
      .get('/about', (req, res) => {
        const { query } = parse(req.url, true);
        app.render(req, res, '/about', query);
      })
      .get('/api/greet', (req, res) => {
        res.json({ name: req.query?.name ?? 'unknown' });
      })
      .get(/_next\/.+/, (req, res) => {
        const parsedUrl = parse(req.url, true);
        handle(req, res, parsedUrl);
      })
      .listen(3000, () => console.log('server ready'));
  } catch (err) {
    console.log(err.stack);
  }
}

main();
  • next를 렌더링 하려면 2번째 get 처럼 app.render 함수를 이용한다.

  • 3번째 server.get처럼 api/greet가 nextjs의 페이지로 연결하지않고 가짜 api를 제공한다.

  • 4번째 get은 개별 서버로 next 프로젝트를 렌더링하게 되면 _next/로 시작하는 정적자원을 nextjs가 처리해야하는데 정규 표현식으로 _next로 시작하는 파일에서 호출해서 오류 방지

Fastify 서버

  • 다른 프레임워크에 비해 속도가 빠르다.
  • 플러그인과 미들웨어를 쉽게 개발할 수 있는 시스템 제공
  • next가 랜더링한 라우트를 관리하는 공식 플러그인이 있음 (fastify-nextjs)

커스텀 서버 배포 요구사항

vercel이나 netlify같은 서비스에 배포할 수 없음
(서버리스 펑션으로 할 순 있으나 복잡)



Chapter.9 테스트

테스트 정의 및 테스트 프레임워크

  • 단위 테스트(jest)
    각 함수가 제대로 작동하는지 확인하는 테스트 , 입력값을 주고 결과가 예상과 일치하는지
  • e2e 테스트 (cypress)
    사용자 상호작용을 흉내내 특정 동작 발생시 적절한 응답을 하는지
    (웹사이트에서 돌아가는지 확인하는것과 비슷한다고 생각하면 된다.)
    form이 제대로 작동하는지 , ui표시가 제대로 됐는지, html요소를 마운트하는지
  • 통합 테스트
    함수나 모듈같이 서로 다른 영역이 함께 잘 작동하는지
    두 개의 함수 조합이 잘 동작하는지, 서로 연관된 함수와 모듈이 잘 작동하는지
  • 테스트 러너 : 모든 테스트를 찾고 결과를 수집해 콘솔에 표시할 수 있는 것 jest나 cypress는 모두 이 테스트 러너의 종류이다.

테스트 환경 구성

  • jest 가장 유명한 테스트 러너임
  • ESNext 기능을 사용하려면 바벨(.babelrc) 수정해야함
    {
    	"presets" : ["next/bable"]
    }
  • .test.js // .spec.js 로 끝나는 모든 파일에 작동하도록 설정된다.
describe("cutTextToLength cuts a string when it's too long", () => {
  test('Should cut a string that exceeds 10 characters', () => {
    const initialString = 'This is a 34 character long string';
    const cutResult = cutTextToLength(initialString, 10);
    expect(cutResult).toEqual('This is a ...');
  });

  test("Should not cut a string if it's shorter than 10 characters", () => {
//	  ~~~
  });
});

Jest 내장함수

  • describe
    테스트와 관련된 그룹을 만든다. (ex. 동일 함수에 대한 여러개의 테스트나 모듈을 여기에 포함 시킨다.)
  • test
    테스트 선언 및 실행
  • expect
    함수의 출력과 예상한 결과를 비교한다.
/**
 * @jest-environment jsdom
 */

describe('ArticleCard', () => {
  test('Generated link should be in the correct format', () => {
    const component = render(<ArticleCard {...article} />);
    const link = component.getByRole('link').getAttribute('href');
    expect(link).toBe('/articles/healthy-summer-meloncarrot-soup-u12w3o0d');
  });
}
  • jest 만으로 리액트 컴포넌트를 테스트 할 수 없음 >> react testing library를 사용해야함
  • rtl은 document 전역 변수를 사용하는데 nodejs가 이를 제공하지 않아 오류 발생할 수 있음
    • jest환경을 jsdom으로 바꿔 문제 해결(테스트 파일 최상단에 선언)

Cypress를 이용한 e2e 테스트

  • yarn add -D cypress 로 설치
  • cypress.json 만들기 (어디서 테스트를 실행하는지 알려주는 설정)
    {
    	"baseUrl":"http://localhost:3000"
    }
  • 일반적으로 최상단 디렉토리 아래에 cypress 디렉터리를 생성하고 진행한다.
describe('articles APIs', () => {
	// 1
  it(`should correctly set application/json header`, () => {
    cy.request('http://localhost:3000/api/articles')
      .its('headers')
      .its('content-type')
      .should('include', 'application/json');
  });
	// 2	
	it(`should correctly return a 200 status code`, () => {
    cy.request('http://localhost:3000/api/articles')
			.its('status').should('be.equal', 200);
  });
	// 3
	it(`should correctly return a list of articles`, (done) => {
    cy.request('http://localhost:3000/api/articles')
      .its('body')
      .each((article) => {
        expect(article).to.have.keys('id', 'title', 'body', 'author', 'image');
        expect(article.author).to.have.keys('id', 'name');
        expect(article.image).to.have.keys('url', 'author');
        done();
      });
  });
	// 4
  it(`should correctly return a an article given an ID`, (done) => {
    cy.request('http://localhost:3000/api/article?id=u12w3o0d').then(({ body }) => {
      expect(body).to.have.keys('id', 'title', 'body', 'author', 'image');
      expect(body.author).to.have.keys('id', 'name');
      expect(body.image).to.have.keys('url', 'author');
      done();
    });
  });
}
  1. 응답 http 헤더에 content-type = application/json 정보가 있는지 확인하는 것
  2. 상태값이 200이 나오는지 확인
  3. each를 통해 api응답으로 객체 배열을 받았을때 반복문을 사용할 수 있음 (단, 끝났을때 done()을 호출해줘야한다.)
  4. each를 이용한 해당 게시물 읽어오는 api 테스트
  • cypress는 start-server-and-test 라이브러리를 통해 서버가 만들어지고 접근 가능할때 cypress를 실행할 수 있도록 해준다.
    • packages.json의 scripts에 추가
      “e2e” : “start-server-and=test ‘yarn build && yarn start’ [http://localhost:3000](http://localhost:3000) cypress”
profile
FE 개발자다?

0개의 댓글