자바스크립트 함수 실행 시간 측정
자바스크립트에서 함수 실행 시간을 측정할 수 있는 다양한 방법에 대해서 정리|13 minutes ago
자바스크립트 함수 실행 시간 측정
함수 실행 시간을 측정할 수 있는 여러 방법에 대해서 학습하고 정리한다. 함수 실행 시간 측정을 하는 방법에 대해서 알고 있으면, 성능 최적화 및 벤치마킹 / 디버깅 등에 도움이 될 수도 있다.
Date.now()
Date 객체의 now 메서드를 사용하면 밀리초를 리턴받을 수 있는데, 이걸 이용해 함수 실행 시간을 측정할 수 있다.
function run() {
const start = Date.now()
for (let i = 0; i < 1e6; i++) {
Math.sqrt(i)
}
const end = Date.now()
console.log(`Execution time: ${end - start} ms`)
}
// result: Execution time: XX ms
now 메서드는 1970년 1월 1일 0시 0분 0초부터 현재까지 경과된 시간을 밀리초 단위로 리턴한다. 이를 통해서 함수 시작시 한번 찍고, 종료부에 한번 찍고 종료 시간과 시작 시간을 빼주면 함수 실행 시간을 측정할 수 있다.
console.time() / console.timeEnd()
Date.now 메서드보다 더 간결한 방식으로, Date 객체와 마친가지이지만 브라우저 및 Node.js 환경 모두에서 사용 가능하다.
function run() {
console.time('Execution Time')
for (let i = 0; i < 1e6; i++) {
Math.sqrt(i)
}
console.timeEnd('Execution Time')
}
// result: Execution Time: XX ms
메서드의 인수로 문자열을 받는데, 라벨링을 한다고 보면 된다. time 메서드와 timeEnd 메서드에 동일한 라벨을 넘겨주면, 해당 라벨에 대한 실행 시간을 측정하여 출력해준다. now 메서드와 마찬가지로 밀리초 단위로 반환된다.
process.hrtime
process 객체가 존재하는 Node.js 환경에서만 사용 가능한 방법이나, 매우 정밀하게 측정할 수 있는 특징이 있다.
function run() {
const start = process.hrtime()
for (let i = 0; i < 1e6; i++) {
Math.sqrt(i)
}
const end = process.hrtime(start)
console.log(`Execution time: ${end[0]}s ${end[1] / 1e6} ms`)
}
// result: Execution time: 0s XX ms
hrtime 은 나노초 단위로 반환한다. 반환하는 타입이 [number, number] 형태이고, 첫번 째는 초를 뜻하고 두번째는 나노초를 의미한다.
performance.now()
위 hrtime 과 동일하게 정밀한 측정이 가능하다는 특징이 있고, 브라우저 환경과 Node.js 환경 모두에서 사용 가능하다. 반환하는 단위는 소수점을 포함한 밀리초다. (hrtime 이 더 세밀한 단위)
function run() {
const start = performance.now()
for (let i = 0; i < 1e6; i++) {
Math.sqrt(i)
}
const end = performance.now()
console.log(`Execution time: ${end - start} ms`)
}
// result: Execution time: XX.XXXX ms
다른 것들과 특징은, 소수점 아래 자릿수가 세밀하여 마이크로초까지 측정할 수 있다는 특징이 있다.
헷갈리는 맛 쿠키
맨날 봐도 맨날 헷갈리는 쿠키에 관련된 정리 내용|a day ago
헷갈리는 맛 쿠키
인증 세션같은 것을 개발하다 보면 늘 접하는 개념이 쿠키인데, 알고있는 것 같으면서도 헷갈리는 부분이 많아 늘 문서를 찾아보게 된다. 이번에 또 접한 쿠키 관련 이슈가 있어 정리하고 기록해보면 좋을 것 같다.
같은 도메인을 결정하는 기준
쿠키는 도메인 단위로 볼 수 있다. 예를 들어, abc.com 에서 설정한 쿠키의 속성 및 값이 def.com 에서 설정한 쿠키의 속성 및 값과 동일해도, 서로 다른 쿠키라고 보는 것이다. 하지만 여기서 동일한 도메인이라는 개념이 헷갈리기 쉬운데, 여기서 짚고 넘어가면 좋을 것 같다. 정리하기 전, 쿠키에서 도메인이 동일한 경우 SameSite 라고 지칭하면 될 것 같다.
abc.com과abc.abc.com은 SameSite로 본다.abc.com과abc.com:8080은 SameSite로 본다.abc.com과abc.co.kr은 SameSite로 보지 않는다.github.io와my.github.io는 SameSite로 보지 않는다.
1번과 4번의 내용이 서로 상충되지 않냐고 볼 수 있는데, SameSite 를 나누는 기준이 public suffix에 명시된 최상위 도메인과 관련이 있는데, 최상위 도메인이 명시되어 있으면, 그 앞의 부분까지 하나의 Site 라고 판단한다. 예를 들어, .com 최상위 도메인은 public suffix 에 명시되어 있으므로 abc.com 을 하나의 Site 라고 판단하고, abc.abc.com, def.abc.com 도 동일한 Site 라고 본다. 반면, .io 는 public suffix 에 명시되어 있지 않으므로 my.github.io 까지 하나의 Site 라고 본다. 즉 your.github.io 와 my.github.io 는 서로 다른 Site 라고 판단되어, SameSite 로 보지 않는다.
SameSite 속성별 특징
SameSite 속성에 값은 총 3가지 중 하나가 올 수 있는데, 각각의 특징을 알고 있어야 한다.
Strcit/ 쿠키를 오직 동일 Site 에서 설정한 경우에만 전송한다. 예를 들어,abc.com에서Set-Cookie로 설정한 쿠키는def.com로 전송하지 못하는 것이다.Lax/ 쿠키를 동일 Site 에서 설정한 경우에만 전송하지만, 조건을 만족하는 경우 다른 Site 여도 쿠키를 전송한다.fetchAPI를 사용한 요청일 경우<img>,<script>등의 태그를 사용한 리소스 요청인 경우 혹은<iframe>내부에서 네비게이션이 일어나는 경우
None/ 쿠키를 동일 Site, 다른 Site 구분 없이 항상 전송한다. 단,Secure라는 속성과 함께 설정되어야 한다. (https를 통한 요청에만 쿠키가 전송되며, localhost 는 예외처리 된다.)
Proxy
뜬금없이 무슨 Proxy냐고 하면.. 최근 회사에서 서비스 인증 방식을 바꾸는 작업에서 JWT를 사용하다 쿠키 방식으로 변경할 때에 서버와 도메인이 달라 쿠키 관련 이슈가 생겼었다. https 를 사용하지 않고 있어서 SameSite=None; 방식을 사용하기에는 무리가 있었고, 보안적으로 문제가 될 부분이라고 생각됬다.
배포 환경에서는 백엔드 서버말고 자기 자신을 호출하게끔 하여 /api 로 시작하는 요청에 대해 nginx 에서 proxy pass 하여 백엔드로 가게 해두어 동일 도메인으로 취급되는 것 같아 이슈가 재현이 되지 않는데, 개발 서버에서는 그 역할을 proxy 가 대신 해줄 수 있었다. 모두 proxy 를 사용하여 API 호출을 사용할 거라 생각해서 동일 도메인이라 쿠키가 당연히 전송될거라 생각했었는데, proxy 를 사용하지 않고 있었던 것이다..! 하여튼 proxy 설정을 통해 동일 도메인 취급을 받을 수 있었다.
인증 방식 자체가 JWT로 가는 경우가 많기도 하다보니, 쿠키 관련은 조금 생소한 느낌이 들기도 했다.
TypeScript erasableSyntaxOnly에 대한 학습과 정리
TypeScript erasableSyntaxOnly 옵션을 학습하고 정리해보기|4 months ago
TypeScript erasableSyntaxOnly에 대한 학습과 정리
타입스크립트 최근 버전에서 사용할 수 있는 erasableSyntaxOnly 옵션은 자바스크립트 런타임에서 지울 수 있는 타입스크립트 구문만 허용한다는 의미이다. 여기서 "지울 수 있는 타입스크립트 구문"에 대한 명칭이 다소 생소한데, 생각해보면 바로 알 수 있는 개념이다.
interface Student {
name: string
grade: string
score: number
}
const student: Student = {
name: 'Lee',
grade: 'A',
score: 96
}
예를 들어 위 타입스크립트 내용을 자바스크립트로 컴파일시 지울 수 있는 구문을 제외한 자바스크립트만 남는다.
const student = {
name: 'Lee',
grade: 'A',
score: 96
}
인터페이스 선언 내용은 지울 수 있는 타입스크립트 구문이기에 제거되고 실행 가능한 자바스크립트 구문만 남는다.
이러한 지울 수 있는 타입스크립트 구문은 타입, 인터페이스 등 컴파일 전 정적 타입 확인시에만 필요한 부분들은 타입스크립트 컴파일시 모두 제거된다. 런타임시에는 필요가 없는 부분이기 때문이다. 그렇다면 지울 수 없는 타입스크립트 구문에는 뭐가 있을까?
지울 수 없는 타입스크립트 구문
런타임시에도 영향을 끼치는, 즉 런타임에도 필요한 타입스크립트에서만 사용한 구문은 반드시 제거되지 않고 남게 된다.
enum HTTPMethod {
GET = 'get',
POST = 'post'
}
const method = HTTPMethod.GET
enum이 그 대표적인 예인데, 해당 타입스크립트 구문은 자바스크립트에 그대로 남아있게 된다.
var HTTPMethod
;(function (HTTPMethod) {
HTTPMethod['GET'] = 'get'
HTTPMethod['POST'] = 'post'
})(HTTPMethod || (HTTPMethod = {}))
const method = HTTPMethod.GET
자바스크립트로 컴파일되어도 enum이 남아있는 부분을 볼 수 있다.
function call() {
console.log('called');
}
namespace Http {
call();
export type Method = string;
}
---> 자바스크립트로 컴파일시
function call() {
console.log('called');
}
var Http;
(function (Http) {
call();
})(Http || (Http = {}));
namespace 선언도 마찬가지로, 자바스크립트로 컴파일되어도 남게된다.
이외에도 타입스크립트 클래스에서 생성자에 public 키워드를 달아 멤버를 초기화하는 shorthand 방식도 자바스크립트로 컴파일시 남아있기 되기 때문에 런타임에서 지울 수 없는 타입스크립트 구문이라고 볼 수 있다.
다시 돌아와서, --erasableSyntaxOnly 타입스크립트 옵션은 이러한 지울 수 없는 타입스크립트 구문을 허용하지 않는다는 의미이다. "지울 수 있는" 타입스크립트 구문만 허용한다. 타입스크립트 공식 문서에서는, Nodejs v23.6 부터 타입스크립트를 바로 실행할 수 잇도록 지원하기 시작한다는데, "지울 수 있는 타입스크립트 구문"을 포함한 타입스크립트 파일만 직접 실행할 수 있다고 이야기한다. 즉, 해당 옵션을 반드시 사용해야만 타입스크립트 파일을 직접 실행할 수 있는 것 같다.
Motion으로 splitText 구현하기
Motion 라이브러리를 이용해 splitText 애니메이션 구현하고 정리하기|7 months ago
splitText 애니메이션
framer motion에 대해서 아는 바는 없지만 이 라이브러리가 motion으로 이전된 것 같다. React/Vue용 라이브러리 및 범용 바닐라 자바스크립트를 지원한다. 여러 트랜지션 및 애니메이션에 대한 유틸리티/컴포넌트를 제공하고, CSS로 구현하기 귀찮은 여러 부분에 대해 알아서 처리해주는 유틸리티가 존재한다.
사용자의 제스쳐에 관련된 컨트롤도 가능하다는 것이 특장점 중 하나이다. 예를 들어, 사용자가 스크롤 할 때, 컨텐츠에 마우스를 올렸을 때, Viewport에 컨텐츠가 들어왔을 때 등 사용자와의 Interaction 부분도 다양하게 관리할 수 있다.
유틸리티 기능 중 splitText라는 기능이 있는데, 텍스트가 점진적으로 나타나는 효과를 주며 모던하고 깔끔한 느낌을 주는 유틸리티 기능이다. 해당 기능이 유료 버전에 한해서만 지원하는 것 같아, 직접 구현해보고 정리한다.
Idea
글자의 철자 분리를 통해 opacity 속성과 x, y 속성을 조작하면 쉽게 구현 가능해보였다.
function splitText(container, text) {}
철자 분리 함수의 시그니처는 이 정도, 글자들을 포함하고 있는 엘리먼트(container)와 텍스트 애니메이션 적용 대상인 텍스트를 인자로 받는다.
function splitText(container, text) {
const lines = text.split('\n')
const textEls = []
for (const line of lines) {
const lineEl = document.createElement('p')
let temp = ''
function push(pushBlank = false) {
const textEl = document.createElement('span')
textEl.textContent = temp
temp = ''
lineEl.appendChild(textEl)
textEls.push(textEl)
if (pushBlank) {
const blank = document.createTextNode(' ')
lineEl.appendChild(blank)
}
}
for (const char of line) {
if (char === ' ') {
push(true)
continue
}
temp += char
}
if (temp) push(false)
container.appendChild(lineEl)
}
return textEls
}
텍스트는 ABC ABC ABC\nABCD ABCDE ABC 형태로 받고, 줄바꿈 문자 단위로 라인별 문자열을 배열에 담고 있다가, 라인별로 단어 사이를 잘라 단어 단위로 애니메이션 적용을 하는 걸로 선회했다. (철자 단위 애니메이션은 뭔가 조잡하고 내용이 애니메이션에 치우친 느낌이라 전달하고자 하는 내용이 확실히 전달되지 못하는 느낌이 든다. 음... 내용을 깔끔하고 모던하게 전달하는게 아니라, 내가 만든 애니메이션을 한번 볼래? 라는 느낌?) 그리고 단어 단위 엘리먼트를 만들어 라인 단위 엘리먼트 하위에 넣는다. 이러면 단어 단위의 엘리먼트에 애니메이션을 적용하면 문서에 있는 애니메이션과 비슷한 느낌을 구현할 수 있을 것 같다.
const text =
'Lorem ipsum dolor sit amet,\nconsectetur adipiscing elit. Vestibulum imperdiet mauris\nornare turpis semper, non egestas leo ultricies.\nPraesent sed laoreet ex. In eget orci arcu.'
const { animate, stagger } = Motion
function animateText() {
return animate(splitText(document.querySelector('.container'), text))
}
애니메이션 실행 함수까지 만들어주면 준비는 끝났다. stagger 메서드는 앞서 말했듯 애니메이션 대상이 복수개인 경우 상대적인 딜레이(앞 애니메이션이 실행되고 지연 시간)를 할당할 수 있게 해주는 유틸리티다. 이 기능이 해당 이펙트의 핵심 유틸리티다.
애니메이션 심화
inView 유틸리티는 HTML Intersection API를 활용해 애니메이션 대상이 viewport에 진입했는지 여부에 따라 애니메이션 실행 여부 결정을 관리할 수 있다. 제작한 애니메이션에 이 유틸리티를 적용해본다.
const { motion, stagger, inView } = Motion
inView(document.querySelector('.container'), el => {
animateText()
return leaving => {
animate(document.querySelector('.container'), { opacity: 0 })
}
})
첫번째 인자로 애니메이션 대상을 넘기고, 두번째 인자로 콜백 함수를 넘기면 엘리먼트가 viewport에 진입했을 때 애니메이션이 수행된다. 그리고, viewport에서 엘리먼트가 벗어나면 리턴한 콜백 함수가 실행된다. 즉, 여기서 viewport 벗어남 애니메이션을 수행하면 적절하다.
글씨 크기가 커서 작은 화면에서는 이상하게 보이는데, 큰 화면에서는 제대로 보인다. 얼추 예상한 것과 비슷하게 동작하므로 만족한다. 이 구현 과정을 통해 motion 라이브러리의 각각의 애니메이션 대상에 상대적인 딜레이를 넣을 수 있는 유틸리티인 stagger에 대해서 알게되었고, inView 메서드를 통해 Intersection API를 쉽게 활용할 수 있는 방법도 알게 되었다. 그리고 심화 과정을 통해, stagger의 딜레이를 조정하면 좀 더 다양한 애니메이션을 구현할 수 있는 걸 알게 되었다.
간단한 자바스크립트 배열 원소 정렬
자바스크립트 기본 제공 함수를 이용해 배열 내 원소를 정렬하는 방법|6 years ago
일반적인 배열 정렬
const arr = ['c', 'a', 'b', 1]
for (const i of arr) {
console.log(i.toString().charCodeAt())
}
// output: 99
// 97
// 98
// 49
문자열과 숫자로 이루어진 간단한 배열이 있다. 배열의 원소들을 모두 읽어들여 문자열로 변환한 뒤 해당 문자의 UTF-16 코드를 출력한다. Array.prototype.sort 함수는 매개변수로 정렬 방식을 정하지 않으면 기본적으로 이 코드를 기준으로 정렬한다.
const arr = ['c', 'a', 'b', 1, '나', '가', '다']
for (const i of arr) {
console.log(i.toString().charCodeAt())
}
arr.sort()
console.log(...arr)
/** result
* 99 (c)
* 97 (a)
* 98 (b)
* 49 (1)
* 45208 (나)
* 44032 (가)
* 45796 (다)
* 1 'a' 'b' 'c' '가' '나' '다'
**/
배열 원소에 숫자, 영어 알파벳, 한글이 있다. 각각 UTF-16 코드로 해당 숫자를 가지므로 이 숫자를 기준으로 정렬하게 되면 1, 'a', 'b', 'c', '가', '나', '다' 순이 된다. 이 함수는 기본적으로 이 함수를 호출하는 배열 자체를 바꾼다. 반환 값도 정렬 된 배열을 반환한다.
const numArr = [1, 100000, 99]
numArr.sort()
console.log(...numArr)
// 1 100000 99
문제점이 하나 존재한다. 배열 정렬 함수를 지정하지 않으면 배열의 모든 원소를 문자열로 바꿔 정렬을 시도한다. 그렇기 때문에 위의 배열에서 기대하는 값 1, 99, 100000은 출력되지 않는다.
정렬 함수 지정
위의 문제를 해결하기 위해서 정렬 함수를 지정한다. Array.prototype.sort 함수는 매개변수로 2개의 매개변수(여기서는 a,b)를 갖는 함수를 받으며 이 함수에서 양수를 반환하면 b가 더 낮은 값으로 정렬, 음수를 반환하면 a가 더 낮은 값으로 정렬 됨.
만약 이 함수가 0을 반환하면 a, b 두 값에 대해 변경하지 않고 다음 요소로 넘어감 이 정렬 함수 방식을 이용해서 위의 숫자 정렬을 다시 해보면 아래와 같이 된다.
const numArr = [1, 100000, 99]
numArr.sort((a, b) => (a > b ? 1 : -1))
console.log(...numArr)
// 1 99 100000
수가 오름차순으로 정렬된다. 반환하는 수를 거꾸로하면(부등호를 반대로 하면) 내림차순으로 정렬된다. 배열의 모든 원소가 사칙연산이 가능하다면 아래와 같은 방식도 가능하다.
const numArr = [1, 100000, 99]
numArr.sort((a, b) => a - b)
console.log(...numArr)
// 1 99 100000
반대로 더한 값을 반환하면 내림차순으로 정렬한다.
객체 정렬
배열 안의 모든 원소가 객체일 경우 객체의 어떤 값을 기준으로 정렬이 가능하다.
const personArr = [
{
name: 'Kim',
age: 30,
job: 'Teacher'
},
{
name: 'Lee',
age: 24,
job: 'Student'
},
{
name: 'Hwang',
age: 35,
job: 'Zookeeper'
}
]
위처럼 객체가 3개 들어있는 배열이 있다. 이 배열의 객체 값들 중 나이순으로 이들을 정렬하고 싶다고 하면 아래와 같이 a, b 객체의 age 속성을 서로 비교하면 된다.
personArr.sort((a, b) => a.age - b.age)
console.log(...personArr)
// { name: 'Lee', age: 24, job: 'Student' } { name: 'Kim', age: 30, job: 'Teacher' } { name: 'Hwang', age: 35, job: 'Zookeeper' }