Custom Log

 

src/middleware/logger/logger.service.ts

import { ConsoleLogger, Injectable } from "@nestjs/common";

@Injectable()
export class LoggerService extends ConsoleLogger {

    constructor() {
        super()
        this.setLogLevels(['verbose', 'debug', 'log', 'warn', 'error'])
    }

    appLog(message: string) {
        this.log(message)
    }

    errorLog(message: string) {
        this.error(message);
    }

}

 

src/middleware/logger/logger.module.ts

import { Module } from "@nestjs/common";
import { LoggerService } from "./logger.service";

@Module({
    providers: [LoggerService],
    exports: [LoggerService]
})
export class LoggerModule {}

src/api/api.module.ts  →  imports: [LoggerModule]

import { Module } from '@nestjs/common';
import { ApiController } from './api.controller';
import { ApiService } from './api.service';
import { LoggerModule } from '@src/middleware/logger/logger.module';

@Module({
  imports: [LoggerModule],
  controllers: [ApiController],
  providers: [ApiService]
})
export class ApiModule {}

 

src/api/api.controller.ts

import { Controller, Get } from '@nestjs/common';
import { LoggerService } from '@src/middleware/logger/logger.service';

@Controller('api')
export class ApiController {

    constructor(private loggerService: LoggerService) {
        loggerService.setContext(ApiController.name)
    }

    @Get('app')
    app() {
        this.loggerService.appLog('application log')
    }

    @Get('error')
    error() {
        this.loggerService.errorLog('error log')
    }

}

API 호출
console log 확인 완료

 


변수로 선언해서 사용

 

main.ts

const logger = new LoggerService();
logger.setContext('main.ts')


async function bootstrap() {

	...
    logger.appLog(`load main.ts`)
	...

}
bootstrap();

 


Middleware Log

미들웨어를 통해 API Request 요청 로그를 남겨보자

 

src/middleware/logger/logger.express.ts

import { Injectable, NestMiddleware } from "@nestjs/common";
import { LoggerService } from "./logger.service";
import { NextFunction } from "express";

@Injectable()
export class LoggerExpress implements NestMiddleware {

    constructor(private loggerService: LoggerService) {
        this.loggerService.setContext(LoggerExpress.name)
    }

    use(req: Request, res: Response, next: NextFunction) {
        this.loggerService.verbose(`${req.url} [${req.method}] ${JSON.stringify(req.body)}`);
        next();
    }

}

 

src/middleware/logger/logger.fastify.ts

import { Injectable, NestMiddleware } from "@nestjs/common";
import { LoggerService } from "./logger.service";
import { FastifyReply, FastifyRequest } from "fastify";

// $ npm i fastify
@Injectable()
export class LoggerFastify implements NestMiddleware {

    constructor(private loggerService: LoggerService) {
        this.loggerService.setContext(LoggerFastify.name)
    }

    use(req: FastifyRequest['raw'], res: FastifyReply['raw'], next: () => void) {
        this.loggerService.warn(`${req.url} [${req.method}]`);
        next()
    }

}

 

src/app.module.ts

import { MiddlewareConsumer, Module, NestModule, RequestMethod } from '@nestjs/common';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import { ApiModule } from './api/api.module';
import { LoggerFastify } from './middleware/logger/logger.fastify';
import { LoggerModule } from './middleware/logger/logger.module';
import { LoggerExpress } from './middleware/logger/logger.express';

@Module({
  imports: [ApiModule, LoggerModule],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule implements NestModule {
/*
    path: '*'
    method: RequestMethod. ALL, GET, HEAD 등
*/
  configure(consumer: MiddlewareConsumer) {
    consumer.apply(LoggerExpress) // 또는 LoggerFastify
      .forRoutes({ path: '/api/*', method: RequestMethod.ALL })
  }

}

API 요청하기
console log 확인

 


참고 링크

https://docs.nestjs.com/techniques/logger

https://docs.nestjs.com/techniques/performance

 

Nest 프로젝트의 package.json - scripts의 테스트 명령어를 보면 Jest 테스트 라이브러리를 사용한다

테스팅 파일명에는 .spec 또는 .test 의 접미사가 있어야 한다

 

describe, it, test, expect

describe() 테스트 모듈을 그룹화하거나 계층별로 나눌 수 있고, it(), test(), expect()**** 키워드를 통해 테스트 코드를 작성한다

it()test() 키워드의 alias이며, 대체하여도 동일하게 작동한다

 

 

beforeAll, afterAll, beforeEach, afterEach

beforeAll(), afterAll() 는 테스트 파일의 시작과 끝에 한번 실행된다. 전역 변수를 관리할 수 있다

beforeEach(), afterEach() 는 테스트 파일 내 테스트 케이스의 시작과 끝에 실행된다

 

 

실행 순서

beforeAll → 
	[beforeEach → (describe - *it) → afterEach] → 
    	・・・ 
    →  [beforeEach → (describe - *it) → afterEach] 
→ afterAll

메서드 compile() 는 비동기식이므로 대기해야 합니다. 모듈이 컴파일되면 메서드를 사용하여 모듈이 선언하는 모든 정적 인스턴스(컨트롤러 및 공급자)를 검색할 수 있습니다

 

 

 

 

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();
    await app.init();
  });

단위 (Unit) 테스트

 

api.service.ts

import { Injectable } from '@nestjs/common';

@Injectable()
export class ApiService {

    /*
        get
    */
    getOne(id: number) {
        return id
    }

    getEmptyArr() {
        return []
    }

    getPlusOne(num: number) {
        return num + 1;
    }

    /*
        update
    */
    update(b: boolean) {
        return !b;
    }

}

 

api.service.spec.ts

import { Test, TestingModule } from '@nestjs/testing';
import { ApiService } from './api.service';
import { describe } from 'node:test';

describe('ApiService', () => {
  let service: ApiService;

  beforeEach(async () => {
    const module: TestingModule = await Test.createTestingModule({
      providers: [ApiService],
    }).compile();

    service = module.get<ApiService>(ApiService);
  });

  it('should be defined', () => {
    expect(service).toBeDefined();
  });

  describe('get', () => {
    it('should return id', () => {
      const id = 1;
      const ret = service.getOne(id);

      expect(ret).toEqual(id);
    })

    it('should return empty array', () => {
      const ret = service.getEmptyArr();

      expect(ret).toEqual([]);
    })
  })

  describe('update', () => {
    it('should return falsy', () => {
      const b = true;
      const ret = service.update(b);

      expect(ret).toBeFalsy();
    })
  })

});

 

테스트

$ npm run test

 

$ npm run test:cov

 

api.service.ts % Funcs 수치를 보면 75라고 나와있다. service.ts 파일의 함수는 총 4개인데, service.spec.ts에서는 3개의 함수의 테스팅 코드만 작성하였다

 

 

나머지 함수의 테스트 코드도 작성한 후, 실행하면 %Funcs 수치가 100으로 나온다

 


통합 (e2e, End to End) 테스트


npm run test:e2e 명령어를 통해 통합 테스트를 진행할 수 있다.

 

main.ts

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);
  app.useGlobalPipes(
    new ValidationPipe(
      {
        whitelist: true,
        forbidNonWhitelisted: true,
        transform: true
      }
    )
  )
  await app.listen(3000);
}
bootstrap();

 

test/app.e2e-spec.ts

  beforeAll(async () => {
    const moduleFixture: TestingModule = await Test.createTestingModule({
      imports: [AppModule],
    }).compile();

    app = moduleFixture.createNestApplication();

    app.useGlobalPipes(
      new ValidationPipe(
        {
          whitelist: true,
          forbidNonWhitelisted: true,
          transform: true
        }
      )
    )

    await app.init();
  });

 

main.ts에 app에 대한 설정 (ex.GlobalPipes)을 했다면, app.e2e-spec.ts 파일에도 동일하게 설정해 주어야 한다

 

 


참고 링크

https://docs.nestjs.com/fundamentals/testing

https://nomadcoders.co/nestjs-fundamentals

https://jestjs.io/docs/api

'JavaScript > NestJS' 카테고리의 다른 글

[NestJS] 환경 변수 (config) 사용하기  (0) 2023.08.20
[NestJS] Logging 하기  (0) 2023.08.19
[NestJS] 경로 alias 설정하기  (0) 2023.08.17
[NestJS] API 생성하기 (+ CORS)  (0) 2023.08.17
[NestJS] 시작하기  (0) 2023.08.17
import myCode from 'src/folder1/folder2/folder3/folder4/folder5/index

간단한 프로젝트는 상관 없지만, 프로젝트 규모가 커지면 코드에 표현되는 경로가 깔끔하지 않을 수 있음

절대 경로(alias)를 설정

 


첫번째 방법

tsconfig.json

{
  "compilerOptions": {
    ...
    "baseUrl": "./",
    ...
    "paths": {
      "@src/*": [
        "./src/*"
      ],
      "@src/api/*": [
        "./src/api/*"
      ]
    }
  }
}

두번째 방법

tsconfig.paths.json

{
  "compilerOptions": {
    "baseUrl": "./",
    "paths": {
      "@src/*": ["./src/*"],
      "@app/*": ["./src/app/*"],
      "@loader/*": ["./src/loader/*"],
      "@middleware/*": ["./src/middleware/*"]
    }
  }
}

tsconfig.json

{
  "compilerOptions": {
    ...
  },
  "extends": "./tsconfig.paths.json"
}

경로 적용 확인

app.modules.ts 파일 변경

변경 전
import { ApiModule } from './api/api.module';

변경 후
import { ApiModule } from '@src/api/api.module';

혹시 watch 모드로 실행되고 있다면 적용되지 않을 수 있다. 서버를 재실행하여 다시 확인해 보자

 


Jest 테스트를 위한 경로 설정

tsconfig.json 파일 설정으로는 *spec.ts 파일의 경로 alias가 적용이 되지 않는다

package.json

{
    ...
  "jest": {
    ...
    "moduleNameMapper": {
      "^@src/(.*)$": "<rootDir>/$1"
    }
  }
}

jest 테스트 경로 alias가 적용되었다

'JavaScript > NestJS' 카테고리의 다른 글

[NestJS] 환경 변수 (config) 사용하기  (0) 2023.08.20
[NestJS] Logging 하기  (0) 2023.08.19
[NestJS] Jest 테스트하기  (0) 2023.08.17
[NestJS] API 생성하기 (+ CORS)  (0) 2023.08.17
[NestJS] 시작하기  (0) 2023.08.17


NestJS는 강력한 CLI (Command-Line Interface)를 지원한다.

CLI 를 통해 API를 만들기 위한 모듈(module, mo), 컨트롤러(controller, co), 서비스(service, s)를 생성해보자


# CLI 통한 생성 방법
$ nest (generate|g) (name|alias) name


import { Controller, Get, Head } from '@nestjs/common';

@Controller('api')
export class ApiController {

    @Head()
    foo() {}

    @Get()
    get(): string {
        return 'hello World!'
    }

}

데코레이터(@)를 선언하여, API의 경로와 메서드를 정의할 수 있다.
* HEAD 메서드를 사용할 때, 동일한 경로의 GET 메서드가 존재한다면, 반드시 HEAD 메서드를 먼저 선언해야 한다
데코레이터는 중요하지만, 함수명은 의미가 없다. (하지만 함수명을 마음대로 짓자는 뜻은 아니다, 클린 코드


Express 모듈 사용하기

import { Controller, Get, Req, Request, Res, Response } from '@nestjs/common';

@Controller('api')
export class ApiController {

    @Get('express1')
    get1(@Request() request, @Response() response) {
        response.status(200).send(`${request.method} ${request.url} ${request.ip}`)
    }

    @Get('express2')
    get2(@Req() req, @Res() res) {
        res.status(200).json({
            method: req.method,
            url: req.url,
            ip: req.ip
        })
    }

}


Query, Param, Body 사용하기

import { Body, Controller, Get, Param, Post, Query } from '@nestjs/common';

@Controller('api')
export class ApiController {

    @Get('param/:id')
    getParam(@Param('id') id) {
        return `id: ${id}`
    }

    @Get('params/:id/:name')
    getParams(@Param('id') id, @Param('name') name) {
        return `id: ${id}, name: ${name}`
    }

    @Get('query')
    getQuery(@Query('id') id) {
        return `id: ${id}`
    }

    @Post('body')
    postBody(@Body() body) {
        return body
    }

}


Put, Patch, Delete, HttpCode, Redirect

import { Body, Controller, Delete, Get, HttpCode, Patch, Put, Redirect } from '@nestjs/common';

@Controller('api')
export class ApiController {

    @Put()
    put(@Body() body) { }

    @Patch()
    patch() { }

    @Delete()
    @HttpCode(204)
    delete() { }

    @Get('redirect')
    @Redirect('https://nestjs.com', 301)
    getRedirect() {}

}

CORS  설정

 

첫번째 방법

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule);

  app.enableCors({ origin: "*", methods: "GET" })

  await app.listen(3000);
}
bootstrap();

 

두번째 방법

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

async function bootstrap() {
  const app = await NestFactory.create(AppModule, {
    cors: {
      origin: ["*"]
      , optionsSuccessStatus: 200
      , maxAge: 300
      , credentials: true
    }
  });

  await app.listen(3000);
}
bootstrap();

 


참고 링크

NestJS Documentation - Custom decorators

https://docs.nestjs.com/controllers

 

'JavaScript > NestJS' 카테고리의 다른 글

[NestJS] 환경 변수 (config) 사용하기  (0) 2023.08.20
[NestJS] Logging 하기  (0) 2023.08.19
[NestJS] Jest 테스트하기  (0) 2023.08.17
[NestJS] 경로 alias 설정하기  (0) 2023.08.17
[NestJS] 시작하기  (0) 2023.08.17

Nest makes use of robust HTTP Server frameworks like Express (the default) and optionally can be configured to use Fastify as well!

  • Nest(NestJS)는 효율적이고 확장 가능한 Node.js 서버 (백엔드) 애플리케이션을 구축하기 위한 프레임워크
    • TypeScript, JavaScript 지원
    • HTTP 프레임워크를 활용하여 구성
      • Express: JavaScript로 작성되고 Node.js 런타임 환경에서 구동되는 인기 있는 웹 프레임워크
      • Fastify: 최대 효율성과 속도를 제공하는 데 중점을 둔 고성능 및 낮은 오버헤드 프레임워크
    • 프로그래밍 요소 결합
      • OOP (Object Oriented Programming) : 객체 지향 프로그래밍
      • FP (Functional Programming) : 함수형 프로그래밍
      • FRP (Functional Reactive Programming) : 함수 반응형 프로그래밍, 비동기 데이터 스트림 이용
    • CLI (Command-Line Interface) 지원

 

 

설치, 프로젝트 생성

# i: install , g: global , n: new
$ npm i -g @nestjs/cli
$ nest (n|new) <project_name>
$ npm install

Node 버전 16 이상에서만 NestJS 설치 가능, "node -v" 명령어로 버전 확인!
프로젝트 my-project 생성

 

프로젝트 기본 구조

동작 과정은 main.js → app.module.ts → app.controller.ts → app.service.ts로 진행


어플리케이션의 시작점&nbsp; main.ts

 

NestFactory.create( )  매개변수에 프로젝트에서 사용할 루트 모듈이 들어간다. 기본 구조에서는 app.module.ts 이 들어가있지만 커스텀 모듈을 지정할 수 있다
* 메인 함수명이 bootstrap 일 필요는 없다 (함수명 변경 가능)


Module (모듈)

Provider와 Controller들을 논리적인 기능이나 도메인에 따라 하나로 묶어주는 역할을 하며, 재사용성을 높여줌

AppModule 에서는 데코레이터(@, Decorator) 를 사용해 컨트롤러와 프로바이더(서비스)를 명시

이유는  제어의 역전(IoC, Inversion of Control) 기술 중 하나인 의존성 주입(DI, Defendency Injection)을 하기 위함


Controller (컨트롤러)

외부와의 통신, 라우팅을 담당

constructor (생성자) 매개변수 선언을 통해 AppModule에서 선언한 의존성 주입을 사용할 수 있음


Provider (공급자)

Nest에서 가장 간단하고 작은 단위의 컴포넌트를 표현
비즈니스 로직
기본적으로 @Injectable() 데코레이터로 나타내며,  다른 프로바이더나 컨트롤러에 주입(Inject)될 수 있음
종류로는 가장 간단한 Service부터 Middleware, Pipe, Guard, Interceptor 등이 존재

*spec.ts 파일들은 테스트를 하기 위한 파일이다.

test 폴더에 있는 app.e2e-spec.ts 파일을 통합 테스트를 하기 위함

src 아래 위치에 속해있는 다른 spec.ts 파일은 유닛 테스트를 하기 위한 파일들이다.

 

package.json의 scripts 확인하여 테스트를 진행

$ npm run test
$ npm run test:watch
$ npm run test:cov
$ npm run test:debug
$ npm run test:e2e

Mac의 터미널(Terminal)을 통해 진행하다 보면 권한(permission) 문제 때문에, 막혀서 sudo 명령어를 계속 입력해 줘야 할 수 있다.
공부를 하기 위해서 chmod 명령어를 통해 권한을 열어주도록 하자

# '~'는 사용자의 폴더 경로를 뜻한다. (ex. /Users/sejin )
# 프로젝트에서 모듈 설치 시 (ex. npm install)
$ chmod -R 755 ~/.npm

# my-project는 내가 생성한 nest 프로젝트 폴더
# 프로젝트 실행 시 (ex. npm run start:dev)
$ chmod -R 755 my-project

참고 링크

NestJS Documentation
Nomadcoders Nest

'JavaScript > NestJS' 카테고리의 다른 글

[NestJS] 환경 변수 (config) 사용하기  (0) 2023.08.20
[NestJS] Logging 하기  (0) 2023.08.19
[NestJS] Jest 테스트하기  (0) 2023.08.17
[NestJS] 경로 alias 설정하기  (0) 2023.08.17
[NestJS] API 생성하기 (+ CORS)  (0) 2023.08.17

PM2 (Process Manager)는 JavaScript 런타임 Node.js의 프로세스 관리자이다.

설치

# Install
npm install pm2@latest -g
yarn global add pm2
# Update
pm2 update

 

어플리케이션 실행

pm2 start app.js

 

프로세스 확인

pm2 [list|ls|status]

 

로그 모니터링

# To display logs in realtime
pm2 logs
# Here is a realtime dashboard that fits directly into your terminal
pm2 monit
# Web based dashboard, cross servers with diagnostic system
pm2 plus

pm2 monit

 

설정 파일

# You can also create a configuration file, called Ecosystem File, 
# to manage multiple applications.
pm2 ecosystem
# And start it easily
pm2 start ecosystem.config.js
// ecosystem.config.js
module.export = {
     apps: [{
      name: 'app_name',
      script: './app.js',
      instances: 0,
      exec_mode: 'cluster'
    }]
}

instances 값을 0으로 설정하면 CPU 코어 수 만큼 프로세스를 생성하겠다는 뜻이다.

 

프로세스 개수 늘리기

# 프로세스 4개 추가
pm2 scale app_name +4 
# 프로세스 4개 제거
pm2 scale app_name 4

 

프로세스 관리

pm2 stop <app_name> # 또는 파일 경로 (ex. /home/server.js)
pm2 delete <app_name>
pm2 restart <app_name>
pm2 reload <app_name>
pm2 show <app_name>

 

 


 

참고 사이트

오류

C:\Users\tpwls\vscode> npx create-next-app@latest
npm ERR! syscall lstat
npm ERR! path C:\Users\tpwls\AppData\Roaming\npm
npm ERR! enoent ENOENT: no such file or directory, lstat 'C:\Users\tpwls\AppData\Roaming\npm'
npm ERR! enoent This is related to npm not being able to find a file.
npm ERR! enoent 

npm ERR! A complete log of this run can be found in: C:\Users\tpwls\AppData\Local\npm-cache\_logs\2023-07-28T15_22_52_433Z-debug-0.log

해결 방법

C:\Users\tpwls\vscode> npm i -g npx
PayloadTooLargeError: request entity too large

413 Payload Too Large

Body 요청 시, JSON 길이가 길면  클라이언트는 413 코드 응답을 받게되고, 서버에서는 PayloadTooLargeError 가 발생한다.

 

app.use(express.json({
  limit: "50mb"
}))

위 코드를 추가하여 해결

		for (int i = 0; i < numbers.length; i++) {
			for (int j = i + 1; j < numbers.length; j++) {
				if (numbers[i] < numbers[j]) {
					numbers[i] = numbers[j];
					break;
				}
			}
		}

처음에는 위와 같이 이중 for문을 사용하여 풀었다.

하지만 시간복잡도는 O(N^2) (N = 1,000,000) 로 당연히 시간 초과가 났다.

 

아래 코드와 같이 우선 순위 큐를 사용하여, 문제를 풀 수 있었다.

유사한 문제로는 백준 알고리즘의 [오큰수] 라는 문제가 있다. 

 

다른 블로그의 풀이를 참고하였고, 예전에 백준에서 푼 기억이 있는데 도저히 방법이 생각 안났다.

import java.util.PriorityQueue;

class Solution {
	public int[] solution(int[] numbers) {
		
		// 작은 숫자부터 큐 우선순위로 넣는다
		PriorityQueue<int[]> q = new PriorityQueue<>((a, b) -> a[1] - b[1]);

		for (int i = 0; i < numbers.length; i++) {
			int v = numbers[i];

			// 큐가 비어있지 않고, 큐에서 꺼난 값이 현재 인덱스의 값보다 작으면 실행
			// 큐에 들어있다는 것은 앞순서에서 넣었고 아직 정해지지 않은 것
			// 현재 인덱스의 값과 현재 큐에 들어있는 값을 비교한다
			while (!q.isEmpty() && q.peek()[1] < v) {
				// Queue에 넣은 numbers의 값들은 이미 사용했기 때문에
				// 값을 변경해도 상관없다
				numbers[q.poll()[0]] = v;
			}
			
			// 큐에 넣는다
			q.add(new int[] { i, v });
		}

		// 아직도 큐에 남아있다는 것은 오른쪽에 자기보다 큰 수가 없기 때문에
		// -1로 값 지정
		while (!q.isEmpty()) {
			numbers[q.poll()[0]] = -1;
		}

		return numbers;
	}
}

 

 

유사 문제

https://www.acmicpc.net/problem/17298

 

17298번: 오큰수

첫째 줄에 수열 A의 크기 N (1 ≤ N ≤ 1,000,000)이 주어진다. 둘째 줄에 수열 A의 원소 A1, A2, ..., AN (1 ≤ Ai ≤ 1,000,000)이 주어진다.

www.acmicpc.net

 

Lv.3 억억단을 외우자 : 프로그래머스

문제 설명

영우는 천하제일 암산대회를 앞두고 있습니다. 암산보다는 암기에 일가견이 있는 영우는 구구단을 확장하여 억억단을 만들고 외워버리기로 하였습니다.
억억단은 1억 x 1억 크기의 행렬입니다. 억억단을 외우던 영우는 친구 수연에게 퀴즈를 내달라고 부탁하였습니다.
수연은 평범하게 문제를 내봐야 영우가 너무 쉽게 맞히기 때문에 좀 어렵게 퀴즈를 내보려고 합니다. 적당한 수 e를 먼저 정하여 알려주고 e 이하의 임의의 수 s를 여러 개 얘기합니다. 영우는 각 s에 대해서 s보다 크거나 같고 e 보다 작거나 같은 수 중에서 억억단에서 가장 많이 등장한 수를 답해야 합니다. 만약 가장 많이 등장한 수가 여러 개라면 그 중 가장 작은 수를 답해야 합니다.
수연은 영우가 정답을 말하는지 확인하기 위해 당신에게 프로그램 제작을 의뢰하였습니다. e와 s의 목록 starts가 매개변수로 주어질 때 각 퀴즈의 답 목록을 return 하도록 solution 함수를 완성해주세요.

제한 사항

  • 1 ≤ e ≤ 5,000,000
  • 1 ≤ starts의 길이 ≤ min {e,100,000}
  • 1 ≤ starts의 원소 ≤ e
  • starts에는 중복되는 원소가 존재하지 않습니다.

분석

억억단에서 s 보다 같거나 크고, e 보다 같거나 작은 수를 구하고, 그 중 가장 많이 등장한 수를 구하여야 하고, 등장 수치가 같으면 작은 값을 선택한다.

풀이

  • e의 범위가 5,000,000 이기 때문에 억억단의 나올 수 있는 가장 큰 수치는 5,000,000 ^ 2이다. 하지만 5,000,000 초과된 값은 우리가 찾아야하는 값이 아님
  • 시간을 조금 줄이기 위해 값을 찾을 때 억억단 표의 반만 사용하였음. i * j 를 확인하면서, i == j인 경우 1을 카운트, i != j 인 경우, i * j, j * i 두 가지 방식이 나오기 때문에 2를 카운트
  • Map(값, 등장 수치)을 구한 뒤, List에 넣어 등장 수치가 많은 순 (같다면 값이 작은 순)으로 정렬
  • starts 배열 값을 확인하면서 List index 0 부터 확인하면서, 값이 s 이상인 것을 찾음

 

import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;

class Solution {
    public int[] solution(int e, int[] starts) {
        int size = starts.length;
        int[] answer = new int[size];

        Map<Integer, Integer> map = new HashMap<Integer, Integer>();

        for (int i = 1; i <= e; i++) {
            for (int j = 1; j <= i; j++) {
                long value = i * j;
                if (e < value)
                    break;
                if (i == j) {
                    map.put(i * j, map.getOrDefault(i * j, 0) + 1);
                } else {
                    map.put(i * j, map.getOrDefault(i * j, 0) + 2);
                }
            }
        }

        ArrayList<int[]> list = new ArrayList<int[]>();
        for (int key : map.keySet()) {
            list.add(new int[] { key, map.get(key) });
        }

        list.sort(new Comparator<int[]>() {
            @Override
            public int compare(int[] o1, int[] o2) {
                if (o1[1] == o2[1])
                    return o1[0] - o2[0];
                return o2[1] - o1[1];
            }
        });

        for (int a = 0; a < size; a++) {
            for (int i = 0; i < list.size(); i++) {
                if (starts[a] <= list.get(i)[0]) {
                    answer[a] = list.get(i)[0];
                    break;
                }
            }
        }

        return answer;
    }
}

+ Recent posts