728x90
반응형

존경하는 개발자분께 PHP를 개발할때 도움이 되는 문서를 추천받았다.
PHP를 개발하는데 있어서 많은 것을 느끼고 반성할 수 있어서 남기고자 한다!

https://modernpug.github.io/php-the-right-way/

 

PHP: The Right Way 한국어판

쉽게 읽을 수 있는 PHP의 베스트 프랙티스, 코딩 표준, 권위있는 튜토리얼로의 링크를 제공합니다.

modernpug.github.io

1995년에 PHP가 라스무스 러돌프가 처음 공개했는데, 그 이후로 웹 개발에 큰 발전을 가져왔다.
30년 가까운 시간동안 쓰인 언어만큼 웹 상에 PHP과 관련한 무지하게 많은 레퍼런스들이 탄생했다.
그만큼 정확하지 않은 정보가 많았는데 잘못된 PHP 개발 방식을 바로잡고, 과거와 많은 것이 달라진 PHP 8.1 기준으로 현대에 클린하게 PHP를 개발할 수 있는 방법을 가이드하는 문서다.

PHP는 PSR이라고 공식적으로 표준화된 코드 스타일을 제안하고 있는데, 이를 기반으로 단순히 코드 스타일 뿐만 아니라 OS에 따라 PHP를 다루는 솔루션이나 보안, 배포, DB, 문서화 등 여러 방면에서 이상적인 것들을 제안하는 문서라서 PHP 개발 입문이든, PHP를 쓰던 사람이든 한번 읽어보면 많은것을 느낄 수 있다.

 

사설로 작성된 문서인 만큼 뭐든 무조건적인 정설은 아니니 참고하는 것이 좋을 것 같다!

728x90
반응형
728x90
반응형

기존의 백엔드에서 스키마 변경이 이루어지면 DB에서 수정 후, 코드를 수정했다.

나는 여태까지 그렇게 수정해왔는데, Sequelize의 sync기능을 이용하면 model만 수정해도 DB가 알아서 수정되는 마술이 있다는 것을 발견했다.

 

Sequelize는 DML(데이터 조작)뿐만 아닌 DDL(데이터 정의)도 지원한다는 특징 덕분인데 


이러한 특징 덕분에 이미 만들어진 테이블을 모델에 매핑할 수 있을 뿐 아니라, 작성한 모델을 바탕으로 테이블을 생성하는 등 다룰 수 있다.

아래는 sync를 적용하는 방법이다.

'use strict';

const _sequelize = require('sequelize');
const _config = require('config');

const models = require('../models/index');

const sequelize = new _sequelize(
    _config.get('mysql_local.database'),
    _config.get('mysql_local.username'),
    _config.get('mysql_local.password'),
    {
        host: _config.get('mysql_local.host'),
        dialect: _config.get('mysql_local.dialect'),
        logging: (message) => {
            console.log(message);
        },
        timezone: _config.get('mysql_local.timezone'), // Asia/Seoul
    },
);

// DB 스키마 Sync
sequelize.sync({force: true})

 

이런 방법이 운영중인 서비스에 적용하는 것은 당연히 문제가 있고, 서비스를 만들어가는 과정에서 스키마 구조를 변경하거나 개편할 때 편의에 따라 사용하면 될 것 같다.

기존 테이블을 지우고 재생성하면 데이터를 모두 지운다는 점에서 치명적일 수 있으니 꼭 작업 전에 고려하고 진행해야한다!

728x90
반응형
728x90
반응형

기존에 pdf 데이터를 만들기위한 라이브러리로 fpdf를 사용했었다.

그러나 fpdf는 유니코드를 제공안해서 utf-8로 변환해서 사용해왔는데,
랲, 벑, 숧 과 같이 조합이 독특한 문자를 해결할 수 없어서 유니코드를 적용하기 위해 알아보던 중, 

tfpdf라는 라이브러리가 기존 fpdf 라이브러리의 확장된 유니코드 사용 가능한 라이브러리인 것을 발견했다. 이를 활용해서 tfpdf 라이브러리로 변경하여 작업을 했는데 아래와 같은 오류가 발생했다. 

 

Fatal error: Uncaught Exception: tFPDF error: Some data has already been output, can't send PDF file in /var/www/swc-####/lib/tfpdf/tfpdf.php:276 Stack trace: #0 /var/www/swc-####/lib/tfpdf/tfpdf.php(1267): tFPDF->Error('Some data has a...') #1 /var/www/swc-####/lib/tfpdf/tfpdf.php(1219): tFPDF->_checkoutput() #2 /var/www/swc-smartsms.aligo.in/shop/order_sheet.html(304): tFPDF->Output('D', 'test.pdf', true) #3 {main} thrown in /var/www/swc-####/lib/tfpdf/tfpdf.php on line 276

 

"일부 데이터가 이미 출력되었습니다. PDF 파일을 보낼 수 없습니다"라는 의미를 갖고 있다. 따로 출력하는 코드도 없어서 의아하긴 했지만 ,, 출력버퍼 오류인거같아서 출력버퍼를 파일 다운로드 직전에 추가하였다.

ob_end_clean();
$pdf->Output('D', 'test.pdf', true);

이렇게 수정하니 정상적으로 작동하는데, 전에 엑셀 관련 작업했을때도 비슷한 경험이 있다.

파일 추출, 다운로드와 같은 작업을 할땐 출력 버퍼의 모든 내용을 정리해야겠다!

 

참고 : http://www.lug.or.kr/files/docs/PHP/function.ob-end-clean.html

728x90
반응형

'IT > PHP' 카테고리의 다른 글

2023년 기준 요즘 PHP 가이드는?  (1) 2023.04.28
PHP Fatal error 컴포저 관련 에러  (0) 2023.02.10
PSR(PHP Standards Recommendations 표준 규약) 2  (0) 2021.05.07
728x90
반응형

이미 winston으로 error나 warning은 추적이 되도록 셋팅되어있지만, 발생한 정확한 쿼리는 알 수 없다.

그래서 쿼리를 저장하고자 하는데,
경험상 파일시스템에 쿼리를 로깅하는것이 빠르고 부하도 적긴 하지만 분석이 쉽지 않았다. 

때문에 사이즈가 작은 프로젝트에는 DB에 저장하는 방식을 사용해서 큰 문제가 없을 것이라고 예상되어, 쿼리를 DB에 로깅하는 방식을 구현해보고자 한다.

쿼리를 DB에 로깅하는 방식은 흔하지 않을 것이라고 생각되지만, 프로젝트의 특징에 따라 필요한 경우가 있다고 생각한다. 이번에 직접 경험해보면서 장단점을 파악해보려고 한다 !!

 

1. 로그 테이블 생성

가장 기본적인 데이터인 id, 쿼리, 시간만 담을 수 있도록 로그 테이블을 생성했다.

CREATE TABLE `logs` (
  `id` int NOT NULL AUTO_INCREMENT,
  `message` text NOT NULL,
  `createdAt` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  `updatedAt` timestamp NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb3;

 

2. Sequelize 셋팅

const sequelize = new _sequelize(
    _config.get('mysql_local.database'),
    _config.get('mysql_local.username'),
    _config.get('mysql_local.password'),
    {
        host: _config.get('mysql_local.host'),
        dialect: _config.get('mysql_local.dialect'),
        logging: (message) => {
            console.log(message);
	    // 로깅을 위한 로깅이 안되도록 예외처리
            if (!message.includes('INSERT INTO `logs`')) {
                logToDatabase(message);
            }
        },
        timezone: _config.get('mysql_local.timezone'), // Asia/Seoul
    },
);

// 로그 테이블 생성
const Log = sequelize.define('log', {
    message: {
        type: _sequelize.STRING,
        allowNull: false,
    },
});

// DB에 로그 저장
const logToDatabase = (message) => {
    Log.create({ message });
};

기존 Sequelize 셋팅의 logging 프로퍼티만 일부 수정했다. 기존에는 콘솔에 로깅하는 작업만 되어있었지만, logToDatabase 함수가 추가되어 이제 발생하는 쿼리를 모두 DB에 로깅한다. 

처음에는 로그를 삽입하는 부분을 예외처리 안했더니 아래 사진처럼 로그를 로깅하기위한 무한루프가 발생했다. 때문에 INSERT INTO `logs' 를 포함한 쿼리는 로깅되지 않도록 예외처리했다.

 

728x90
반응형
728x90
반응형

개발하다보면 모든 필드가 필요한 create 이후 모든 필드를 선택사항으로 만드는 update가 필요한 경우가 있다.

이때 Create에 사용한 DTO를 복붙하고 ?를 붙여서 애트리뷰트들은 선택사항으로 만들 수 있지만, Nest.js에서는 이를 쉽게 만들 수 있는 PartialType을 제공한다. PartialType을 적용하는 방법은 다음과 같다.

1. 먼저 @nestjs/mapped-types 를 설치한다.

npm i @nestjs/mapped-types

 

2. Create DTO객체를 extends로 다음과 같이 Update 객체에 연결한다.

import { PartialType } from '@nestjs/mapped-types';
import { CreateMovieDTO } from './create-movie.dto';

export class UpdateMovieDTO extends PartialType(CreateMovieDTO) {}

 

이렇게 쉽게 DTO 입력 유형의 모든 속성이 선택 사항으로 설정된 유형을 만들 수 있다 !

 

참고 : https://docs.nestjs.com/openapi/mapped-types#partial

728x90
반응형
728x90
반응형

ValidationPipe는 Application 수준에서 바인딩해서 모든 엔드포인트가 잘못된 데이터를 수신하지 않도록 보호하는 역할을 한다. 다음과 같이 ValidationPipe를 적용할 수 있다!

import { ValidationPipe } from '@nestjs/common';
import { NestFactory } from '@nestjs/core';
import { AppModule } from './app.module';

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    // 유효성 검사를 위한 파이프
    new ValidationPipe({
      whitelist: true,
      forbidNonWhitelisted: true,
      transform: true,
    }),
  );
  await app.listen(3000);
}
bootstrap();
  • whitelist : class-validator의 유효성 검사 데코레이터가 없는 애트리뷰트(속성)는 제거한다. 즉, 무관한, 수집하지 않을 데이터가 수신되면, 무시한다.
  • forbidNonWhitelisted : 데코레이터가 없는 애트리뷰트가 수신되면 에러를 내뱉는다. whitelist에서 보안을 강화한 옵션으로, whitelist만 설정되어있을 경우 데코레이터가 없는 애트리뷰트가 수신되면 에러를 내지 않고 무시하지만, 이 옵션은 에러를 발생시킨다는 차이점이 있다.
    • whitelist 가 true로 적용되어있어야 작동한다.
  • transform : DTO 클래스에 따라 유형이 지정된 객체로 자동으로 변환된다. 기본적으로 애트리뷰트는 string으로 수집되고 옵션이 없으면 string으로 가져오지만, transform 을 활성화 하면 number애트리뷰트는 number로 변환하여 수집한다.

disableErrorMessage 와 같이 에러 메시지 명시를 하지 않는 옵션도 있는데, 자세한 에러 메시지를 비활성화해야하는 일부 프로덕션 서비스에서 사용될 수 있다!

여러 옵션을 적용하여 보안과 편의를 증진시킬 수 있다는 점이 흥미로운 ValidationPipe다!

728x90
반응형

+ 최근 게시글