출근 69일차 - NestJS에서 Oracle

·2022년 12월 1일
0

회사이야기

목록 보기
6/50

회사업무 중 오라클을 쓸 일이 생겼다.

과거에도 적었지만, 조금 더 정돈되게 적어보고 싶어서 하루 일기를 할애해서 적어본다.

사실 프리즈마를 써보고 싶었는데 오라클 지원 안하더라 ㅎㅎ;

주의, 해당 도커파일을 사용할 경우 프로시저는 사용이 불가능합니다.

1. TypeORM에서 오라클 쓰기

NestJS에 TypeORM 베이스로 회사코드가 짜여져있다보니 동일하게 서버를 구축했다.

모듈에 넣는 데이터는 크게 다른 것이 없다.

    TypeOrmModule.forRootAsync({
      name: 'default',
      useFactory: async () => {
        return {
          type: 'oracle',
          host: process.env.HOST,
          port: process.env.PORT,
          sid: process.env.SID,
          username: process.env.USERNAME,
          password: process.env.PASSWORD,
          entities: [],
          dropSchema: false,
          synchronize: false,
          keepConnectionAlive: true,
        } as TypeOrmModuleAsyncOptions;
      },
    }),

특이하게 SID 라는 것이 있는데, 이것은 오라클에서 데이터베이스 묶음(?)을 쓰기 위한 과거의 산물인 것 같다.
최근 오라클 클라이언트를 찾다보니 구식이라고 적혀있었다.

아, 그리고 오라클에는 이넘이 없다는 특징이 있다!

2. TypeORM에서 N개의 데이터베이스 사용하기

여러개의 데이터베이스가 당연히 필연적으로 써야되서 이것도 좀 많이 찾아봤다.

근데 별거 없었음.

    TypeOrmModule.forRootAsync({
      name: 'default',
      useFactory: async () => {
        return {
          type: 'oracle',
          host: process.env.HOST,
          port: process.env.PORT,
          sid: process.env.SID,
          username: process.env.USERNAME,
          password: process.env.PASSWORD,
          entities: [],
          dropSchema: false,
          synchronize: false,
          keepConnectionAlive: true,
        } as TypeOrmModuleAsyncOptions;
      },
    }),
     TypeOrmModule.forRootAsync({
      name: 'second',
      useFactory: async () => {
        return {
          type: 'mysql',
         host: process.env.HOST,
          port: process.env.PORT,
          username: process.env.USERNAME,
          password: process.env.PASSWORD,
          database: process.env.DATABASE,
          entities: [],
          dropSchema: false,
		  synchronize: false,
          keepConnectionAlive: true,
        } as TypeOrmModuleAsyncOptions;
      },
    }),     

바로 데이터베이스의 name에 이름을 적어주는 것이 특징이였다.

한 개는 무조건 default를 선언해줘야하고, 그 외 부터는 자유롭게 적어줘도 된다.

  constructor(
    @InjectDataSource('default')
    private readonly mainDataSource: DataSource
    @InjectDataSource('second')
    private readonly subDataSource: DataSource
  ) {}

그리고 서비스의 생성자에 이름을 넣어서 선언을 해주면 해당하는 것을 사용할 수 있었다.

3. 오라클 DB 연결하기

오라클같은 경우에는 오라클 클라이언트라는 것을 따로 다운받아서, 해당 파일을 적용해줘야한다.

그리고 일단 npm에서 패키지를 가져와야한다.

https://www.npmjs.com/package/oracledb

그렇게 실행을 하면.......

에러가 뜬다.

그래서 위에 링크에 있는 > http://oracle.github.io/node-oracledb/INSTALL.html <
여기 들어가서, 운영체제에 맞는 파일을 다운로드 받으면 된다.

인텔맥이랑 M1이랑 리눅스 알파인이랑 우분투랑 전부 다 다르다.

아무튼 파일을 다운받아서 해당 파일의 경로를 이렇게 넣어줘야지만 실행이 된다.

해당 코드는 데이터베이스를 연결해주는 모듈쪽에 선언해주면 된다.

oracledb.initOracleClient({ libDir:  process.env.HOME+'path' });

4. 프로시저 사용하기

과거에는 개발자들은 데이터베이스에 프로시저로만 사용할 수 있었다고 한다.

프로시저는 SQL을 동작시키는 함수라는 느낌이다.

그래서 결과값이 있음<ㅋㅋ

    return await this.dataSource.manager.query(
      `CALL 시드이름.프로시저이름(
          :입력값1, :입력값2, :입력값3, :입력값4, :입력값5, :입력값6,
          :결과값1, :결과값2, :결과값3)`,
      [
        payload.입력값1,
        payload.입력값2,
        payload.입력값3,
        payload.입력값4,
        payload.입력값5,
        payload.입력값6,
        { type: oracledb.DB_TYPE_VARCHAR, dir: oracledb.BIND_OUT },
        { type: oracledb.DB_TYPE_VARCHAR, dir: oracledb.BIND_OUT },
        { type: oracledb.DB_TYPE_VARCHAR, dir: oracledb.BIND_OUT },
      ]
    );

이러면 결과값이 [결과값1, 결과값2, 결과값3] 이렇게 나온다!

5. Docker에서 오라클 DB 연결하기. With Alpine

저번에 찾았던 깃허브 링크가 있었다.

https://github.com/Shrinidhikulkarni7/OracleClient_Alpine/blob/master/Dockerfile

FROM alpine:latest

RUN apk --no-cache add libaio libnsl libc6-compat curl && \
    cd /tmp && \
    curl -o instantclient-basiclite.zip https://download.oracle.com/otn_software/linux/instantclient/instantclient-basiclite-linuxx64.zip -SL && \
    unzip instantclient-basiclite.zip && \
    mv instantclient*/ /usr/lib/instantclient && \
    rm instantclient-basiclite.zip && \
    ln -s /usr/lib/instantclient/libclntsh.so.21.1 /usr/lib/libclntsh.so && \
    ln -s /usr/lib/instantclient/libocci.so.21.1 /usr/lib/libocci.so && \
    ln -s /usr/lib/instantclient/libociicus.so /usr/lib/libociicus.so && \
    ln -s /usr/lib/instantclient/libnnz21.so /usr/lib/libnnz21.so && \
    ln -s /usr/lib/libnsl.so.2 /usr/lib/libnsl.so.1 && \
    ln -s /lib/libc.so.6 /usr/lib/libresolv.so.2 && \
    ln -s /lib64/ld-linux-x86-64.so.2 /usr/lib/ld-linux-x86-64.so.2

ENV LD_LIBRARY_PATH /usr/lib/instantclient

그런데 이런 에러가 계--속 발생했다.

이 에러를 해결하려고 내가 이틀을 태우고, 팀원이 다 붙었는데도 해결을 못하다가

개같아서 접으려던 차에 어떤 글을 보게된다.

https://github.com/kubo/ruby-oci8/issues/111

basic으로 수정하면... 문제가 사라진다고....?

응. 사라지던데

FROM node:18-alpine 

RUN apk update && apk --no-cache add libaio libnsl libc6-compat curl && \
    cd /tmp && \
    curl -o instantclient-basiclite.zip https://download.oracle.com/otn_software/linux/instantclient/instantclient-basic-linuxx64.zip -SL && \
    unzip instantclient-basiclite.zip && \
    mv instantclient*/ /usr/lib/instantclient && \
    rm instantclient-basiclite.zip && \
    ln -s /usr/lib/instantclient/libclntsh.so.21.1 /usr/lib/libclntsh.so && \
    ln -s /usr/lib/instantclient/libocci.so.21.1 /usr/lib/libocci.so && \
    ln -s /usr/lib/instantclient/libociicus.so /usr/lib/libociicus.so && \
    ln -s /usr/lib/instantclient/libnnz21.so /usr/lib/libnnz21.so && \
    ln -s /usr/lib/libnsl.so.2 /usr/lib/libnsl.so.1 && \
    ln -s /lib/libc.so.6 /usr/lib/libresolv.so.2 && \
    ln -s /lib64/ld-linux-x86-64.so.2 /usr/lib/ld-linux-x86-64.so.2 

ENV LD_LIBRARY_PATH /usr/lib/instantclient

이 뒤에는 알아서.....

결론 (느낀점?)

많이 사용하는 RDB는 요즘은 2개라고 생각하는 편이다.

Postgres, MySQL

그중에 MySQL을 주력으로 쓰고 있다가, 새로운 데이터베이스를 써봤는데 너무너무너무너무너무너무너무 어려웠다.

Error: ORA-28547: connection to server failed, probable Oracle Net admin error

이 에러가 너무 나를 많이 괴롭혀서 진짜 네트워크부터 별 것을 많이 알아봤는데...
아마 클라이언트 파일마다 구성이 달라서 벌어진 에러로 생각하고 있다.

오라클 클라이언트는 리스너라는 것을 통해서 클라이언트와 서버를 연결해주는 역할을 한다는데
이런 개념도 모르고 있었고, 그저 목적으로만 사용하지 세부적인 디테일을 아는게 너무 없다는 생각이 들었다.

그리고 도커랑 씨름도 엄청 많이하면서 네트워크와 운영체제에 대한 공부를 진짜 해야겠다라는 생각도 많이 들었다.
(리눅스일지도 모르겠지만, 리눅스도 운영체제니까?)

요즘 너무 업무적으로만 일을 하고 있는데, 조금 더 기본에 대한 공부를 할 시간을 조금 줘야할 것 같다.

profile
물류 서비스 Backend Software Developer

0개의 댓글