Mysql 리턴타입 에러 해결: RowDataPacket, ResultSetHeader ( Feat. next.js )

j_wisdom_h·2024년 1월 27일
0

Next.js 프로젝트

목록 보기
4/8

Mysql을 쓰다 보니 리턴값의 타입이
RowDataPacket[], ResultSetHeader 이라는 것을 알게 되었다.

ResultSetHeader을 이용해 insert한 행의 id를 가져오고 싶었는데

Property 'insertId' does not exist on type 'RowDataPacket[] | ResultSetHeader'.
Property 'insertId' does not exist on type 'RowDataPacket[]'

에러가 발생해서 무작정 따라 썼던 db연결 방법을 다시 공부해보기로 했다.
https://sidorares.github.io/node-mysql2/docs/documentation/typescript-examples

[ 원래 코드 ]

import {
    createPool,
    Pool,
    PoolConnection,
    ResultSetHeader,
    RowDataPacket,
} from 'mysql2'

// DB 연결 및 풀 생성
const pool: Pool = createPool({
    host: process.env.DB_HOST || 'localhost',
    user: process.env.DB_USER || 'root',
    password: process.env.DB_PASSWORD || '',
    database: process.env.DB_DATABASE || '',
    port: 3306,
})

// executeQuery 함수
export default function executeQuery(
    query: string,
    arrParams: (string | '')[],
): Promise<RowDataPacket[]> {
    return new Promise((resolve, reject) => {
        pool.getConnection((err: Error, conn: PoolConnection) => {
            if (err) {
                console.error('Error connecting to db:', err.message)
                reject(err)
            } else {
                conn.query(
                    query,
                    arrParams,
                    (
                        err: Error | null,
                        data: RowDataPacket[] | ResultSetHeader,
                    ) => {
                        conn.release()
                        if (err) {
                            console.error(
                                'Error in executing the query:',
                                err.message,
                            )
                            reject(err)
                        } else {
                            console.log('Query executed successfully')
                            resolve(data as RowDataPacket[])
                        }
                    },
                )
            }
        })
    })
}

ConnectionOptions과 PoolOptions

  • ConnectionOptions : 개별적인 MySQL 연결에 대한 구성을 지정한다. 이는 하나의 연결을 위한 옵션들로 구성되며, 한 번에 하나의 연결만을 나타낸다.
  • PoolOptions는 여러 개의 MySQL 연결을 관리하는 데 사용되는 연결 풀을 구성한다.각 연결 풀은 하나 이상의 MySQL 연결을 가지며, 클라이언트가 필요할 때 연결을 가져와 사용할 수 있다.

connection Pool : 데이터베이스와 연결된 Connection을 미리 만들어서 pool 속에 저장해 두고 있다가 필요할 때 Connection을 Pool에서 쓰고 다시 Pool에 반환하는 기법

//DB pool options
const access: PoolOptions = {
    host: process.env.DB_HOST || 'localhost',
    user: process.env.DB_USER || 'root',
    password: process.env.DB_PASSWORD || '',
    database: process.env.DB_DATABASE || '',
    port: 3306,
}

// DB pool create
const pool: Pool = createPool(access)

getConnection

pool.getConnection((err: Error, conn: PoolConnection) => {
    if (err) {
        console.error('Error connecting to db:', err.message)
    } else {
        const res = conn.query('select foo from bar')
        conn.release()
        return res
    }
})

Type

RowDataPacket[]

select, show 쿼리 결과 리턴 값 타입

// SELECT
conn.query<RowDataPacket[]>('SELECT 1 + 1 AS `test`;', (_err, rows) => {
  console.log(rows);
  /**
   * @rows: [ { test: 2 } ]
   */
});

// SHOW
conn.query<RowDataPacket[]>('SHOW TABLES FROM `test`;', (_err, rows) => {
  console.log(rows);
  /**
   * @rows: [ { Tables_in_test: 'test' } ]
   */
});

RowDataPacket[]

Using multipleStatements option as true with multiple queries
다중 쿼리(show, select)에 대한 다중리턴값 타입

const sql = `
  SELECT 1 + 1 AS test;
  SELECT 2 + 2 AS test;
`;

conn.query<RowDataPacket[][]>(sql, (_err, rows) => {
  console.log(rows);
  /**
   * @rows: [ [ { test: 2 } ], [ { test: 4 } ] ]
   */
});

ResultSetHeader

INSERT, UPDATE, DELETE, TRUNCATE

const sql = `
  SET @1 = 1;
`;

conn.query<ResultSetHeader>(sql, (_err, result) => {
  console.log(result);
  /**
   * @result: ResultSetHeader {
      fieldCount: 0,
      affectedRows: 0,
      insertId: 0,
      info: '',
      serverStatus: 2,
      warningStatus: 0,
      changedRows: 0
    }
   */

ResultSetHeader[] : 다중 쿼리(INSERT, UPDATE, DELETE, TRUNCATE)에 대한 다중리턴값 타입

ProcedureCallPacket

/** ProcedureCallPacket */
const sql = 'CALL myProcedure()';

conn.query<ProcedureCallPacket<ResultSetHeader>>(sql, (_err, result) => {
  console.log(result);
  /**
   * @result: ResultSetHeader {
      fieldCount: 0,
      affectedRows: 0,
      insertId: 0,
      info: '',
      serverStatus: 2,
      warningStatus: 0,
      changedRows: 0
    }
   */
});

OkPacke

OkPacket is deprecated and might be removed in the future major release.
Please use ResultSetHeader instead.

제네릭 타입 적용

    const sql =
        'INSERT INTO bridge.post (title, content, num, location, startDate, atTime, duration, endDate, author_id) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)'
    try {
        const res: ResultSetHeader = await executeQuery(sql, [...values])
        console.log('res', res.insertId)
    } catch (error) {
        throw new Error('error')
    }

여기서 res에 에러가 발생했다.

Type 'ResultSetHeader | RowDataPacket[]' is not assignable to type 'ResultSetHeader'.
Type 'RowDataPacket[]' is missing the following properties from type 'ResultSetHeader': affectedRows, fieldCount, info, insertId, and 3 more

res는 executeQuery에 의해 반환 값이 ResultSetHeader | RowDataPacket[]는데 ResultSetHeader을 지정했다는 오류다.

⭐해결법: 제네릭으로 선언 시점이 아닌, 사용 시점에 타입을 선언하자

[최종 수정 코드]

export default function executeQuery<T>(
    query: string,
    arrParams: (string | number | '' | null)[],
): Promise<T> {
    return new Promise<T>((resolve, reject) => {
        pool.getConnection((err: Error, conn: PoolConnection) => {
            if (err) {
                console.error('Error connecting to db:', err.message)
            } else {
                conn.query(query, arrParams, (err, rows) => {
                    conn.release()
                    if (err) {
                        console.error(
                            'Error in executing the query:',
                            err.message,
                        )
                        reject(err)
                    } else {
                        console.log('Query executed successfully')
                        resolve(rows as T)
                    }
                })
            }
        })
    })
}
profile
뚜잇뚜잇 FE개발자

0개의 댓글