Virtual DOM은 무엇인가요? Virtual DOM (VDOM)은 UI의 이상적인 또는 “가상”적인 표현을 메모리에 저장하고 ReactDOM과 같은 라이브러리에 의해 “실제” DOM과 동기화하는 프로그래밍 개념입니다. 이 과정을 재조정이라고 합니다. (React 공식 문서 참조)
(1) 브라우저는 HTML을 전달 받으면 이를 파싱하여 DOM 노드로 이루어진DOM 트리를 생성한다 (2) CSS파일과 각 엘리먼트의 inline 스타일을 파싱, 스타일 정보를 사용해서 Render 트리를 생성한다 (3) 이 노드 스타일을 동기적으로 attachment하고 렌더 트리가 다 만들어지면 레이아웃(reflow) 과정을 통해 노드들에 스크린의 좌표가 주어지며 위치가 정해진다 (4) 렌더링이 끝나면 paint() 메소드를 통해 렌더링된 요소들에 색을 입힘
그렇다면 왜 Virtual DOM은 효율적인걸까?
DOM을 직접 조작한다면 그 때마다 레이아웃의 재계산과 리렌더링이 일어날 것
이 변화가 일어날 때 Virtual DOM에서 계산한다면 이 DOM은 가상 DOM이기 때문에 렌더링되지 않고, 그렇기 때문에 연산 비용의 절감 효과가 있다
Virtual DOM은 DOM fragment를 관리하는 과정을 자동화 및 추상화하고 어떤 부분이 바뀌었는지를 파악해준다
AWS 콘솔에서 몇 분 만에 서비스에 필요한 데이터베이스를 구성할 수 있으며, 서버나 네트워크 OS, DB의 설치 등 모든 작업은 불필요함
DB 인스턴스에 대한 CloudWatch 지표로 모니터링이 가능하며 SNS와 연계해 이벤트가 발생할 때 경보 수신이 가능
데이터베이스 성능 개선 도우미를 제공하며 로드를 유발하는 SQL문과 그 이유를 찾기에 용이함
물리적으로 분리된 가용영역에 standby DB를 운영하며 동기식 복제 및 자동 Failover를 수행함
또한 읽기 전용 DB를 제공(MySQL, MariaDB, PostgreSQL, Oracle에서 지원)하며, 장애 발생 시에 빠른 복구를 위해 읽기 전용 복제본을 마스터로 승격 가능함
매일 전체 인스턴스에 대한 볼륨을 백업, DB변경 로그가 저장 됨 (기본 7일 최대 35일 보관)
AWS Aurora 서비스
클라우드 용으로 구축된 MySQL, PostgreSQL 호환 관계형 데이터베이스
3개의 가용 영역에 복제본을 하나씩 갖는 6벌의 데이터 베이스로 내구성이 매우 높다
Aurora Serverless 서비스가 새로 런칭, 필요할 때만 사용하고 사용하지 않을 때는 Shutdown되는 초당 과금 서비스
Amazon Redshift 서비스
빠르고 강력한 페타 바이트 규모의 데이터 웨어하우스
2PB까지 데이터 확장 가능(SSD 및 SAS 디스크 옵션 존재)
Amazon Dynamo DB 서비스
완전관리형 key-value, 문서 데이터베이스
어떤 규모에서도 10밀리초 미만의 성능 제공
내구성이 뛰어난 다중 리전, 다중 마스터 DB
하루 10조 개 이상 요청 처리 가능
Amazon DocumentDB
JSON 데이터에 최적화된 완전관리형 문서 데이터베이스
MongoDB와 호환되어 기존 MongoDB 드라이버 사용 가능
Amazon ElastiCache
Redis, Memcached와 호환되는 인 메모리 DB
AWS Schema Conversion Tool
기존 DB 스키마를 다른 DB 엔진 스키마로 변환하는 툴
오전 강의 : AWS Aurora 실습
열어보기
CloudFormation Template을 사용해 실습환경 생성 (Stack 생성에 10~20분 정도 걸림)
Stack이 생성되면 출력(Outputs)에 Key-Value가 표시됨
Session Manager를 이용해 EC2 인스턴스에 접속
터미널에서 우분투 유저로 스위칭하기 sudo su -l ubuntu
tail -n1 /debug.log로 인스턴스의 정상 기동 확인 * bootstrap complete, rebootin
env |grep DB로 DBUSER, DBPASS, DBUS_SESSION_BUS_ADDRESS를 각각 확인
mysql client를 사용해 aurora 인스턴스로 접속을 확인 clusterEndpoint는 2번에서 확인 가능
1 2 3 4 5 6 7 8 9
export DBURL=auroralab-mysql-cluster.cluster-cqxoqejlondh.ap-northeast-2.rds.amazonaws.com mysql -h$DBURL -u$DBUSER -p"$DBPASS" -e"SELECT @@aurora_version;"
mysql: [Warning] Using a password on the command line interface can be insecure. +------------------+ | @@aurora_version | +------------------+ | 2.09.1 | +------------------+
스프링을 sudo apt-get install openjdk-11-jdk -y로 설치
PetClinic을 사용하기 위해 클론 및 빌드
1 2 3 4 5
git clone https://github.com/kiwonyoon0701/spring-petclinic.git wget https://shared-kiwony.s3.ap-northeast-2.amazonaws.com/m2.tar.Z tar xvfz m2.tar.Z cd spring-petclinic/ ./mvnw package -Dmaven.test.skip=true
CIDR(Classless Inter-Domain Routing)는 클래스 없는 도메인 간 라우팅 기법으로 1993년 도입되기 시작한 최신의 IP주소 할당 방법으로 CIDR는 기존의 IP 주소 할당 방식이었던 네트워크 클래스를 방법을 대체한 방식. CIDR는 IP Address의 영역을 나눌 때 기존방식보다 유연하게 자신이 원하는 Network Address와 Host Address를 나눌 수 있다.
서브넷 메뉴에서 서브넷 생성으로 추가 서브넷 생성 시작
VPC 아이디에서 방금 생성한 VPC를 선택(추가 서브넷이므로)
CIDR는 기존 서브넷의 IP와 앞자리 두 블럭(고정 범위의 네트워크 주소)이 같도록 하고 뒷 두 블럭(가변 범위의 호스트 주소)이 다르게 설정
VPC 라우팅 테이블을 통해 방금 생성한 VPC의 라우팅 테이블 ID와 동일하도록 설정
보안 그룹 생성하기
보안 그룹 메뉴에서 보안 그룹 생성
보안 그룹 이름과 설명을 작성하고 1번에서 생성한 VPC ID로 설정
인바운드 규칙에 HTTP/SSH을 위치 무관(소스)으로 설정
웹 서버 생성하기
EC2 콘솔에서 인스턴스 시작으로 인스턴스를 생성
Amazon Machine Image(AMI) 선택에서 Amazon Linux 2 AMI를 선택
t2.micro(프리 티어 사용 가능)를 선택한 후, 다음: 인스턴스 세부 정보 구성 버튼을 클릭
네트워크와 서브넷을 선택하고 퍼블릭 IP 자동 할당을 활성화로 설정
고급 세부 정보에 #include https://s3.amazonaws.com/immersionday-labs/bootstrap.sh를 입력
태그 추가까지 넘어가 키와 값을 입력
보안 그룹 구성에서 기존 보안 그룹 선택을 통해 2번에서 만든 보안 그룹을 선택하고 검토 및 시작
새 키 페어 생성을 선택하고 키 페어 이름을 입력한 후 키 페어 다운로드
인스턴스 시작으로 인스턴스를 생성(인스턴스 연결로 연결하여 CLI를 볼 수 있음)
AMI 생성하고 AMI 기반 인스턴스 생성하기
Amazon Machine Image(AMI)는 인스턴스를 시작하는데 필요한 정보를 제공
인스턴스를 선택하고 우측 상단 작업 메뉴에서 이미지 및 템플릿 → 이미지 생성
이미지 생성 페이지에서 이미지 이름을 입력하고 우측 하단의 이미지 생성 버튼을 클릭
왼쪽 AMI 메뉴에서 상태가 available인지 확인하고 시작하기
단계 3에서 VPC 네트워크를 선택하고 추가 서브넷을 서브넷으로 설정 및 퍼블릭 IP 자동 할당을 활성화로 설정
단계 5에서 태그를 추가하여 값을 입력하고 보안 그룹을 설정(3번의 보안 그룹 설정 과정과 동일)
키 페어는 위에서 생성한 키 페어를 사용
로드밸런서(Elastic Load Balancing) 구성하기
로드밸런서는 애플리케이션 트래픽을 EC2 인스턴스, 컨테이너, IP 주소, Lambda 함수, 가상 어플라이언스와 같은 여러 대상에 자동으로 분산시킴
로드 밸런서 메뉴에서 Load Balancer 생성
Load Balancer 유형 선택에서 Application Load Balancer를 선택
1단계에서는 로드 밸런서 이름과 ip 주소 유형으로 ipv4를 선택, 리스너로는 HTTP가 선택되었는지 확인
가용 영역 파트에서 VPC를 선택하고 가용 영역을 체크하여 서브넷들을 선택
단계 3 보안 그룹 구성에서 새 보안 그룹 생성 선택하고 이름과 설명을 작성한 후 HTTP 유형의 위치 무관(소스)를 선택
단계 4 라우팅 구성에서 이름을 작성하고 인스턴스를 대상으로 하는 HTTP 프로토콜로 설정
단계 5 대상 등록에서 두 개의 인스턴스를 선택하고 등록된 항목에 추가
검토 및 생성
보안 그룹 메뉴 → 인바운드 규칙 편집
로드밸런서의 트래픽만 받을 수 있도록 소스 부분에서 사용자 지정 → 로드 밸런서 보안 그룹을 선택
아키텍처 접근법이 품질 속성에 미치는 영향을 판단, 아키텍처의 적합성을 평가하는 모델로 SAAM, ATAM, CBAM, ADR, ARID 등의 모델이 있다
디자인 패턴
소프트웨어 설계에서 공통으로 발생하는 문제에 대해 자주 쓰이는 설계 방법을 정리한 패턴
개발의 효율성, 유지보수성, 운용성이 높아지며 프로그램의 최적화에 도움이 된다
패턴의 이름, 문제 및 배경, 솔루션, 사례, 결과, 샘플 코드 등으로 구성되어 있다
디자인 패턴의 유형은 목적(생성, 구조, 행위) 과 범위(클래스, 객체)로 구성된다
디자인 패턴의 종류 생성 패턴
Builder : 복잡한 인스턴스를 조립하여 만드는 구조로 객체를 생성하는 방법(과정)과 객체를 구현(표현)하는 방법을 분리
Prototype : 처음부터 원형을 만들어놓고 복사 후 필요한 부분만 수정하여 사용하는 패턴
Factory Method : 상위 클래스의 인터페이스를 하위 클래스에서 인스턴스를 생성하도록 하는 방식
Abstract Factory : 제공된 API를 사용하여 생성된 클래스를 Concrete Product 클래스에서 구체적으로 구현하는 패턴
Singleton : 한 클래스에 한 객체만 존재하도록 제한하는 패턴
구조 패턴
Bridge : 기능 계층과 구현 계층을 연계하고 구현부에서 추상화된 부분을 독립적으로 확장할 수 있는 디자인 패턴
Decorator : 기존에 구현된 클래스에 필요한 기능을 추가해 나가는 설계 패턴으로 유연하게 확장 가능한 패턴
Facade : 단순한 인터페이스를 사용해 결합도를 낮추고 시스템 구조 파악을 쉽게 하는 패턴
Flyweight : 클래스의 경량화를 목적으로 하는 디자인 패턴
Proxy : 대리 객체를 사용해 메모리 용량을 절약하고 정보 은닉의 역할도 수행하는 디자인 패턴
Composite : 객체들을 트리 구조로 구성하여 복합 객체와 단일 객체를 모두 동일하게 다루도록 하는 패턴
Adapter : 상속을 이용하는 클래스 패턴과 위임을 이용하는 인스턴스 패턴의 두 가지 형태로 사용되는 디자인 패턴
행위 패턴
Mediator : 객체지향 설계에서 중재자를 두고 통신의 빈도수를 줄여 객체 지향의 목표를 달성하게 해 주는 디자인 패턴
Interpreter : 언어의 구문을 나누어 해석을 맡는 클래스를 각각 작성하여 여러 형태의 언어 구문을 해석할 수 있게 만드는 디자인 패턴
Iterator : 내부 구조를 노출하지 않고 복잡한 객체의 원소를 순차적으로 순회 접근 가능하게 해주는 행위 패턴
Template Method : 상위 클래스에선 추상 메서드로 기능의 골격을, 하위 클래스에선 세부 처리를 구체화 하는 방식의 디자인 패턴
Observer : 객체의 상태 변화에 따라 다른 객체의 상태도 연동되는 일대다 의존 디자인 패턴
State : 객체 상태를 캡슐화하여 클래스화하여 참조케 하는 방식의 디자인 패턴
Visitor : 각 클래스 데이터 구조에서 처리기능을 분리, 별도의 클래스를 만들어놓고 해당 클래스의 메서드가 각 클래스를 돌아다니며 특정 작업을 수행하도록 만드는 패턴
Command : 재사용성이 높은 클래스를 설계하는 패턴으로 하나의 추상 클래스에 메서드를 만들어 각 명령이 들어오면 각 명령이 들어오면 그에 맞는 서브 클래스가 선택되어 실행되는 디자인 패턴
Strategy : 알고리즘을 하나의 클래스로 캡슐화하고 필요할 때서로 교환해서 사용할 수 있게 하는 패턴
Memento : 객체의 정보를 저장할 필요가 있을 때 적용하는 디자인 패턴으로 ‘작업취소(Undo)’를 요청 할 수 있다
Chain of Responsibility : 한 요청을 2개 이상의 객체에서 처리하도록 하는 디자인 패턴
(3) 현행 시스템 분석서 작성 및 검토
현행 시스템 관련 자료 수집 → 수집 자료의 분석 → 분석한 결과를 기반으로 산출물 작성 → 산출물에 대한 검토 수행
(4) 개발 기술 환경 정의
운영체제 시스템 분석
네트워크 시스템 분석
OSI 7계층(응용, 표현, 세션, 전송, 네트워크, 데이터 링크, 물리 계층)
DBMS 시스템 분석
미들웨어 시스템 분석
오픈소스 사용 시 고려 사항
라이선의 종류, 사용자 수, 기술의 지속 가능성 등을 고려
기출문제
다음 보기가 설명하는 패턴을 쓰시오
1 2 3
한 객체의 상태가 바뀌면 그 객체에 의존하는 다른 객체들에 연락이 가고 자동으로 내용이 갱신되는 방법으로 일대다의 의존성을 가지며 상호작용하는 객체 사이에서는 가능하면 느슨하게 결합하는 디자인을 사용해야 한다.
Observer Pattern
Linux 운영체제 위에서 구동하며 휴대폰 전화를 비롯한 휴대용 장치를 위한 운영체제와 미들웨어, 사용자 인터페이스 그리고 표준 응용 프로그램(웹 브라우저, 이메일 클라이언트, 단문 세시지 서비스, MMS) 등을 포함하고 있는 소프트웨어 스택이자 리눅스 모바일 운영체제로 개발자들이 자바와 코틀린 언어로 응용 프로그램을 작성할 수 있게 했고, 컴파일된 바이트 코드를 구동할 수 있는 런타임 라이브러리를 제공하는 운영체제는 무엇인지 쓰시오.
안드로이드
목적에 다른 디자인 패턴의 유형에는 생성, 구조, ( )이/가 있다. 괄호 안에 알맞는 유형을 쓰시오.
행위
예상문제
소프트웨어 아키텍처에 대해서 서술하시오.
여러 가지 소프트웨어 구성요소와 그 구성요소가 가진 특성 중에서 외부에 드러나는 특성, 그리고 구성요소 간의 관계를 표현하는 시스템의 구조나 구조체
4+1 뷰 중 다음 설명에 해당하는 뷰가 무엇인지 쓰시오.
1 2 3
- 시스템의 비기능적인 속성으로서 자원의 효율적인 사용, 병행 실행, 비동기, 이벤트 처리 등을 표현한 뷰 - 개발자, 시스템 통합자 관점
프로세스 뷰
시스템을 계층으로 구분하여 구성하는 패턴으로 서로 마주 보는 두 개의 계층 사이에서만 상호작용이 이루어지는 패턴은 무엇인가?
계층화 패턴
MVC 패턴에 대해 서술하시오.
대화형 애플리케이션을 모델, 뷰, 컨트롤러 3개의 서브 시스템으로 구조화하는 패턴
디자인 패턴에 대해서 서술하시오.
소프트웨어 공학의 소프트웨어 설계에서 공통으로 발생하는 문제에 대해 자주 쓰이는 설계방법을 정리한 패턴
전역 변수를 사용하지 않고 객체를 하나만 생성하도록 하며, 생성된 객체를 어디에서든지 참조할 수 있도록 하고, 한 클래스에 한 객체만 존재하도록 제한하는 디자인 패턴은 무엇인가?
싱글톤 패턴
상위 클래스에서 객체를 생성하는 인터페이스를 정의하고, 하위 클래스에서 인스턴스를 생성하도록 하는 방식으로, 상위 클래스에서는 인스턴스를 만드는 방법만 결정하고, 하위 클래스에서 그 데이터의 생성을 책임지고 조작하는 함수들을 오버로딩하여 인터페이스와 실제 객체를 생성하는 클래스를 분리할 수 있는 특성을 갖는 디자인 패턴은 무엇인가?
팩토리 메서드 패턴
알고리즘 군을 정의하고 같은 알고리즘을 각각 하나의 클래스로 캡슐화한 다음, 필요할 때 서로 교환해서 사용할 수 있게 하는 패턴으로, 행위를 클래스로 캡슐화해 동적으로 행위를 자유롭게 바꿀 수 있게 해주는 디자인 패턴은 무엇인가?
전략 패턴
어떤 작업을 처리하는 일부분을 서브 클래스로 캡슐화해 전체 일을 수행하는 구조는 바꾸지 않으면서 특정 단계에서 수행하는 내역을 바꾸는 패턴으로 일반적으로 상위 클래스(추상 클래스)에는 추상 메서드를 통해 기능의 골격을 제공하고, 하위 클래스(구체 클래스) 메서드에는 세부 처리를 구체화하는 방식으로 사용하며 코드 양을 줄이고 유지보수를 용이하게 만드는 특징을 갖는 디자인 패턴은 무엇인가?
처음엔 각 스킬을 키로, 인덱스를 값으로 갖는 객체를 만들어 풀려 했으나 스킬트리 내 스킬들 인덱스 비교가 어려워서 다른 방법을 찾다가 정규식으로 처리하는 방법을 찾음
1 2 3 4 5 6 7
functionsolution(skill, skill_trees) { let regex = newRegExp(`[^${skill}]`, 'g'); // 정규식으로 skill에 해당하지 않는(^) 글자를 찾게끔하기 let isPossible = skill_trees.map(eachTree => eachTree.replace(regex, '')) // replace를 통해 스킬이 아닌것들을 죄다 빈 문자열로 변환 .filter(each => skill.substring(0, each.length) === each) // 정규식으로 필터링된 각 스킬트리를 substring으로 원래 스킬을 필터링된 문자열의 길이만큼 잘라내고 그 값이 스킬트리와 같은지 비교 return isPossible.length; // isPossible 배열 내에는 배울 수 있는 스킬만 남을 것이므로 배열의 길이를 리턴 }
1. 입력이 빈 문자열인 경우, 빈 문자열을 반환합니다. 2. 문자열 w를 두 "균형잡힌 괄호 문자열" u, v로 분리합니다. 단, u는 "균형잡힌 괄호 문자열"로 더 이상 분리할 수 없어야 하며, v는 빈 문자열이 될 수 있습니다. 3. 문자열 u가 "올바른 괄호 문자열" 이라면 문자열 v에 대해 1단계부터 다시 수행합니다. 3-1. 수행한 결과 문자열을 u에 이어 붙인 후 반환합니다. 4. 문자열 u가 "올바른 괄호 문자열"이 아니라면 아래 과정을 수행합니다. 4-1. 빈 문자열에 첫 번째 문자로 '('를 붙입니다. 4-2. 문자열 v에 대해 1단계부터 재귀적으로 수행한 결과 문자열을 이어 붙입니다. 4-3. ')'를 다시 붙입니다. 4-4. u의 첫 번째와 마지막 문자를 제거하고, 나머지 문자열의 괄호 방향을 뒤집어서 뒤에 붙입니다. 4-5. 생성된 문자열을 반환합니다.
functionsolution(p) { if (p === '') return p; // 1. 입력이 빈 문자열인 경우, 빈 문자열을 반환합니다.
let left = 0, right = 0, pIndex = 0; // 레벨 1의 올바른 괄호처럼 left와 right를 나누기 let isBalanced = true; // "균형잡힌 괄호 문자열"의 flag 변수
do { if (p[pIndex] === '(') left++; else right++; if(right > left) isBalanced = false; pIndex++; } while (left !== right) let u = p.substr(0, pIndex); let v = p.substr(pIndex);
if(isBalanced) { return u + solution(v); } else { u = u.substr(1, u.length - 2).replace(/[\(]|[\)]/g, a => a === ')' ? '(' : ')'); v = `(${solution(v)})`; return v +u; } }
functionsolution(p) { if (p.length < 1) return""; // 1번
let balance = 0; // 재귀적으로 호출될 때마다 0으로 초기화됨 let pivot = 0; do { balance += p[pivot++] === "(" ? 1 : -1// 좌측 괄호라면 1을 아니라면 -1을 더함 } while (balance !== 0); // 0이라면 "균형잡힌 괄호 문자열"
const u = p.slice(0, pivot); // balance가 0이 아니라면 문자열 w를 두 "균형잡힌 괄호 문자열" u, v로 분리 const v = solution(p.slice(pivot, p.length)); // v는 1부터 다시 수행해야 하므로 재귀적으로 함수를 호출 (u로 나눠진 부분 다음부터)
if (u[0] === "(" && u[u.length - 1] == ")") // 첫 괄호와 마지막 괄호를 검사해 "올바른 괄호 문자열"인지 확인 return u + v; else return"(" + v + ")" + reverse(u); }
functionreverse(str) { return str.slice(1, str.length - 1) // u의 첫 번째와 마지막 문자를 제거 .split("") .map((c) => (c === "(" ? ")" : "(")) // 나머지 문자열의 괄호 방향을 뒤집어서 뒤에 붙임 .join(""); // 다시 문자열로 join하여 리턴 }
functionsolution(nums) { let combination = []; let answer = 0; let primeNums = [];
for(let i = 2; i <= 2997; i++){ if(primeNums[i] == false) continue; for(let k = i + i; k <= 2997; k += i) { primeNums[k] = false; } }
for (let i = 0; i < nums.length; i++) { for (let j = i+1; j < nums.length; j++) { for (let k = j+1; k < nums.length; k++){ let temp = nums[i]+nums[j]+nums[k]; combination.push(temp); } } }
for (let i = 0; i < combination.length; i++) { if(primeNums[combination[i]] !== false) answer++ } return answer; }
1. 모든 대문자를 대응되는 소문자로 치환합니다. 2. 알파벳 소문자, 숫자, 빼기(-), 밑줄(_), 마침표(.)를 제외한 모든 문자를 제거합니다. 3. 마침표(.)가 2번 이상 연속된 부분을 하나의 마침표(.)로 치환합니다. 4. 마침표(.)가 처음이나 끝에 위치한다면 제거합니다. 5. 빈 문자열이라면, "a"를 대입합니다. 6. 길이가 16자 이상이면, 첫 15개의 문자를 제외한 나머지 문자들을 모두 제거합니다. 7. 만약 제거 후 마침표(.)가 끝에 위치한다면 끝에 위치한 마침표(.) 문자를 제거합니다. 8. 길이가 2자 이하라면, 마지막 문자를 길이가 3이 될 때까지 반복해서 끝에 붙입니다.
정규식을 사용해 1번부터 8번을 구현하기
4번, 5번을 모르겠어서 다른 분의 풀이를 참고함
정규식은 언제 봐도 어렵다…
1 2 3 4 5 6 7 8 9 10 11
functionsolution(new_id) { const answer = new_id .toLowerCase() // 1. 모두 소문자로 치환 .replace(/[^\w-_.]/g, '') // 2. \w는 밑줄 문자를 포함한 영숫자 문자에 대응 .replace(/\.{2,}/g, '.') // 3. \.{2,}로 2개 이상 연속된다면 '.'로 replace .replace(/^\.|\.$/g, '') // 4. 마침표가 처음과 끝에 있는지 확인 ^\.| \.$(^: 입력의 시작부분, $: 입력의 끝부분) .replace(/^$/, 'a') // 5. 시작(^)과 끝($)이 빈 문자열이라면 a로 replace .slice(0, 15).replace(/\.$/, ''); // 6, 7. slice로 15개 문자만 남기고 4번과 마찬가지로 \.$를 사용해 끝문자열의 마침표 삭제 const len = answer.length; return len > 2 ? answer : answer + answer.charAt(len - 1).repeat(3 - len); // 8. 길이가 2보다 작다면 아이디 끝에 반복해서 붙이기 }
맨 처음 왼손 엄지손가락은 * 키패드에 오른손 엄지손가락은 # 키패드 위치에서 시작하며, 엄지손가락을 사용하는 규칙은 다음과 같습니다. 1. 엄지손가락은 상하좌우 4가지 방향으로만 이동할 수 있으며 키패드 이동 한 칸은 거리로 1에 해당합니다. 2. 왼쪽 열의 3개의 숫자 1, 4, 7을 입력할 때는 왼손 엄지손가락을 사용합니다. 3. 오른쪽 열의 3개의 숫자 3, 6, 9를 입력할 때는 오른손 엄지손가락을 사용합니다. 4. 가운데 열의 4개의 숫자 2, 5, 8, 0을 입력할 때는 두 엄지손가락의 현재 키패드의 위치에서 더 가까운 엄지손가락을 사용합니다. 4-1. 만약 두 엄지손가락의 거리가 같다면, 오른손잡이는 오른손 엄지손가락, 왼손잡이는 왼손 엄지손가락을 사용합니다.
간단한 해시 테이블을 사용하여 키의 위치를 할당하고 번호마다 왼손, 오른손의 현재 위치와의 거리를 구하여 이동하게 함