Overview

보통 백그라운드에서 데이터를 실행할 때는 명령어 마지막에 & 를 붙여서 실행합니다.

$ java -jar my-app.jar &

하지만 위 명령어대로 하면 애플리케이션의 로그를 볼 수 없습니다.

따라서 nohup 을 사용합니다.

nohup 에 대한 자세한 설명과 & 와의 차이점은 https://joonyon.tistory.com/98 에 잘 정리되어 있습니다!


1. nohup 으로 백그라운드 실행

$ nohup java -jar my-app.jar &

[1] 97569
nohup: ignoring input and appending output to 'nohup.out'

2. nohup 로그 조회

이제 nohup.out 파일에 기록되는 로그를 확인할 수 있습니다.

# 로그 조회
$ cat nohup.out

# 로그 테일링
$ tail -f nohup.out

3. 백그라운드 Job 확인

백그라운드에서 실행되는 프로세스가 있는지 확인해볼 수 있습니다.

# 백그라운드에서 실행되는 프로세스 확인
$ bg
-bash: bg: job 1 already in background

# 출력
$ jobs
[1]+  Running                 nohup java -jar my-app.jar &

Reference

Issue

Using default tag: latest
The push refers to repository [docker.io/asdfaasdf/kubia]
6bb2a0932f1d: Preparing 
ab90d83fa34a: Preparing 
8ee318e54723: Preparing 
e6695624484e: Preparing 
da59b99bbd3b: Preparing 
5616a6292c16: Waiting 
f3ed6cb59ab0: Waiting 
654f45ecb7e3: Waiting 
2c40c66f7667: Waiting 
denied: requested access to the resource is denied

Docker Hub 에 이미지를 푸시하려려고 하는 데 아래와 같은 에러 로그가 나타났습니다.

로그를 보면 알 수 있듯이 권한 관련된 이슈인데 이유는 크게 두 가지가 있습니다.

  1. Docker Hub 로그인을 하지 않음
  2. Docker Hub 아이디와 태그된 이미지의 이름이 일치하지 않음

Solution

우선 Docker Hub 에 로그인합니다.

$ docker login           
Login with your Docker ID to push and pull images from Docker Hub. If you dont have a Docker ID, head over to https://hub.docker.com to create one.
Username: bcp0109
Password: 
Login Succeeded

Docker Tag 를 Docker Hub ID 와 동일하게 생성합니다.

$ docker tag kubia bcp0109/kubia

이미지를 푸시합니다.

$ docker push bcp0109/kubia     
Using default tag: latest
The push refers to repository [docker.io/bcp0109/kubia]
6bb2a0932f1d: Pushed 
ab90d83fa34a: Pushed 
8ee318e54723: Pushed 
e6695624484e: Pushed 
da59b99bbd3b: Pushed 
5616a6292c16: Pushed 
f3ed6cb59ab0: Pushed 
654f45ecb7e3: Pushed 
2c40c66f7667: Pushed 

Docker Hub 에 이미지가 올라간 걸 확인할 수 있습니다.

Issue

웹 페이지를 만들 때 고려해야 할 점 중 하나가 시각 장애인을 위한 웹 접근성입니다. (웹 접근성 포스트)

웹 접근성을 적용할 때 iOS 웹뷰에서 실시간으로 변하는 타이머에 초점이 잡히지 않는 이슈가 있었습니다.

처음에는 aria-live 속성을 적용해서 실시간 보이스 낭독을 기대했지만 문장을 다 읽기 전에 시간이 계속 바뀌어서 말이 끊기는 이슈가 있었습니다.

그래서 처음 초점 잡혔을 때의 시간만 안내하길 기대하면서 role="timer" 를 적용했지만 아예 초점이 잡히지 않았습니다.


Solution

답은 role="text" 를 적용하는 거였습니다.

role="text" 는 WAI-ARIA 공식 홈페이지에서도 설명이 없는 걸로 보아 정식 role 은 아닌 것 같습니다.

다만 이 role 만 적용하면 어떤 역할인지 보이스 안내만으론 알기 힘들기 때문에 aria-describedby 속성까지 추가하는 것을 추천합니다.

<span class="screen_out" id="timer_label" aria-hidden="true">남은 시간</span>
<div role="text" aria-describedby="timer_label">10:00</div>

1. Overview

IntelliJ IDEA 를 처음 설치했을때 할만한 세팅과 유용한 플러그인을 알아봅니다.

Ultimate 을 기준으로 합니다.

  • Configuration
    • SDK 설정
    • Auto Import 체크
    • 대소문자 구분 체크 해제
    • Build Memory 늘리기
    • Memory Indicator
    • Always Select Opened File
    • Gradle Build 를 IntelliJ IDEA 로 변경
    • Annotation Processor
    • Inlay Hints
    • 파일 끝에 개행 추가
  • Plugin
    • Key Promoter X
    • Rainbow Brackets
    • CodeGlance
    • GitToolBox

2. Configuration

필수 설정도 있고 단순한 편의 용도도 있습니다.


2.1. SDK 설정

File > Project Structure... > Project SDK 에서 사용 중인 JDK 를 지정합니다.



2.2. Auto Import 체크



2.3. 대소문자 구분 체크 해제

system 을 검색하면 대소문자가 구별되어서 System 이 안나오기 때문에 체크 해제합니다.

대소문자 구분이 필요하면 검색창에서 필터를 추가할 수 있습니다.



2.4. Build Memory 늘리기

빌드할 때 메모리 때문에 실패할 수 있습니다.

Heap Size 를 늘려줍니다.


Help > Edit Custom VM Options.. 에서 추가로 아래 설정을 해주면 좀더 쾌적하게 이용 가능합니다.

(IntelliJ Memory Option 최적화 참고)

-Xmx4096m
-Xms4096m

2.5. Memory Indicator

메모리 정보를 실시간으로 확인하고 싶다면 인텔리제이 우측 하단을 우클릭하고 Memory Indicator 를 체크하면됩니다.



2.6. Always Select Opened File

파일 위치를 검색해서 들어가는 경우 왼쪽 파일 리스트에서 위치를 찾지 못할 때가 있습니다.

Project > Show Options Menu (톱니바퀴) > Always Select Opened File 을 활성화하면 현재 열려있는 파일 위치로 이동시켜줍니다.



2.7. Gradle Build 를 IntelliJ IDEA 로 변경

Gradle 을 사용할 때만 Build 속도를 향상시킬 수 있습니다.



2.8. Annotation Processor



2.9. Inlay Hints

Kotlin 을 사용하는 경우 val, var 를 사용하여 변수를 선언하는데, 타입을 명시하지 않는 경우도 있습니다.

타입을 생략하는 경우 어떤 타입인지 한눈에 안들어올 수가 있는데 Inlay Hints 를 켜면 타입을 알려줍니다.

언어별로 설정할 수도 있으며 저는 그냥 다 켜두는 편입니다.



2.10. 파일 끝에 개행 추가

파일을 저장할때 자동으로 맨 밑에 개행을 추가해주는 기능입니다.



3. Plugin

플러그인은 필수는 아니지만 설치하면 개발 생산성 향상에 도움을 줍니다.


3.1. Key Promoter X

마우스 클릭으로 어떤 액션을 하면 단축키를 알려줍니다.

인텔리제이 단축키를 잘 모르거나 헷갈릴때 익히는 데 도움을 줍니다.



3.2. Rainbow Brackets

여러 개의 괄호가 중첩될 때 색으로 구분해줍니다.



3.3. CodeGlance

코드 우측에 미니맵을 보여줍니다.

파일 크기가 크면 스크롤 할 때 편리하지만 분할해서 볼 때 공간을 차지하기 때문에 호불호가 좀 갈릴 거 같네요.



3.4. GitToolBox

Git 에 관한 지원을 해줍니다.

Inline Blame 이 특히 유용합니다.



Reference

Overview

JavaScript 에서는 마지막 부분을 잘라내는 방법 (drop) 이 여러 가지 있습니다.

그 중에서 가장 일반적으로 사용하는 게 substringslice 인데 둘의 사용법과 차이점을 알아봅니다.


1. substring

string.substring(start, end);

substring 은 이름 그대로 문자열의 일부를 구하는 함수이며 사용법은 위와 같습니다.

잘라내고자 하는 문자열의 시작 (start) 과 끝 (end) 인덱스를 입력합니다.

가장 헷갈릴 만한 점은 start 는 자르는 대상에 포함되고 end 는 포함되지 않습니다.


Example

// 마지막 문자 n 개 버리기
string.substring(0, string.length - n);

마지막 문자들만 버릴 예정이므로 start 는 무조건 0 으로 하고 자를 문자의 갯수만큼 n 을 입력하면 됩니다.


2. slice

string.slice(start, end);

slicesubstring 과 사용법과 문법이 완전히 같습니다.

하지만 단 하나의 차이가 있다면 파라미터로 음수값을 넘길 수 있다는 점입니다.

음수값은 쉽게 이해하자면 -n == string.length - n 으로 생각하면 됩니다.


Example

// 마지막 문자 n 개 버리기
string.slice(0, -n);

음수를 사용할 수 있다는 점 때문에 substring 보다 훨씬 간결합니다.


Conclusion

사용법은 비슷하지만 음수 파라미터의 사용이 가능한 slice 가 훨씬 사용하기 편한 것 같습니다.

StackOverflow 에서는 substring 이 속도가 더 빠르다는 결과도 있었던 것 같은데, 과거의 이야기고 현재는 비슷하다고 하네요.

실제로 벤치마크 가능한 사이트에서 slice vs substr vs substring 을 테스트 해보면 비슷하게 나옵니다.


Reference

Overview

HTML 페이지를 만들 때 고려해야 하는 것 중 하나가 웹 접근성입니다.

웹 접근성이란 시각장애인들이 웹 페이지를 원활하게 이용할 수 있도록 알려주는 가이드라인이라고 생각하면 됩니다.

대표적으로 input 과 label 이 있는데요.

일반 사람들은 별다른 설명 없이 input, button, a 등등의 태그만 있어도 어느정도 이용할 수 있지만 시각장애인들은 직접 클릭할 수도 없이 탭 같은 걸로 각 요소를 이동하는 수밖에 없습니다.

따라서 탭을 눌렀을 때 초점이 어느 순서로 이동하는지, 초점이 잡혔을 때의 안내 메시지가 제대로 나오는지 여부는 굉장히 중요합니다.

이런 설정들을 HTML native 요소만으로는 처리하기 어렵기 때문에 W3C 는 WAI-ARIA 라는 걸 정의했습니다.


1. WAI-ARIA

WAI-ARIA (Web Accessibility Initiative – Accessible Rich Internet Applications) 는 W3C 에서 정의한 기술로 웹 접근성을 위해 지원되는 여러 가지 특성들을 의미합니다.

일반 사용자가 보기에 정상인 화면들도 HTML 요소에 따라서 스크린 리더 등의 보조기기에서 제대로 읽히지 않을 수 있습니다.

WAI-ARIA 는 이를 개선하기 위해 웹 애플리케이션에 역할(Role), 속성(Property), 상태(State) 정보를 추가할 수 있습니다.


1.1. Role

Role 은 HTML 요소의 역할을 정의합니다.

일반적으로는 HTML native 요소만으로 이를 처리하는게 가장 이상적입니다.

버튼은 button, 링크는 a, 체크박스는 input checkbox 등등 이미 존재하는 요소들로도 충분히 표현할 수 있습니다.

하지만 이미지에 버튼 클릭 이벤트를 준다던가 좀더 세세하고 다양한 설정이 하고 싶을 때는 native 요소만으로 부족할 수 있습니다.

이럴 때 role 을 사용해서 해당 요소들의 역할을 명시합니다.

Role 의 종류는 W3C WAI ARIA 의 5.3 Categorization of Roles 에서 확인할 수 있습니다.

<!-- role example -->
<li role="menuitem">Open file…</li>

1.2. State and Property

속성 (Property) 는 해당 요소의 특징이나 상황을 정의하며 aria- 라는 접두사를 사용합니다.

상태 (State) 는 요소의 현재 상태를 나타냅니다.

<!-- property state example 1 -->
<li role="checkbox" aria-checked="true">체크박스 아이템</li>

<!-- property state example 2 -->
<div role="alert" aria-live="assertive">올바르지 않은 입력입니다.</div>

2. ARIA 속성들

공부한 속성들에 대해서 정리합니다.

앞으로도 생길때마다 조금씩 추가할 예정입니다.


2.1. role="checkbox" aria-checked="true"

<span role="checkbox" aria-checked="false" tabindex="0" aria-labelledby="chk1-label"></span> 
<label id="chk1-label">Remember my preferences</label>

<!-- native 요소로 사용할 수 있는 경우 -->
<input type="checkbox" id="chk1-label">
<label for="chk1-label">Remember my preferences</label>

특정 요소에 체크박스 역할을 부여합니다.

aria-checked 를 사용해서 체크됨, 체크안됨 여부를 판단할 수 있습니다.

focus 되지 않는 요소일 경우 tabindex 를 사용하기도 합니다.

가능하면 HTML native checkbox 를 사용하는게 권장되지만 사용할 수 없는 경우에 ARIA 속성을 사용합니다.


2.2. aria-label, aria-labelledby, aria-describedby

특정 요소를 설명하는 데 사용되는 ARIA 속성들입니다.

비슷해보이지만 조금씩 차이가 있습니다.


<button aria-label="menu" class="button"></button>

aria-label 은 우리가 흔히 알고 있는 Label 목적을 위한 속성입니다.

특정 요소에 대한 설명을 그대로 적습니다.

텍스트 대신 그래픽을 사용하는 경우처럼 추가 설명이 필요한 경우 사용할 수 있습니다.


<span id="rg-label">음료수 옵션</span>
<div role="radiogroup" aria-labelledby="rg-label"></div>

aria-labelledbyaria-label 과 비슷하지만 조금 다릅니다.

aria-label 은 어떤 요소에 대한 설명을 직접 적는 반면, aria-labelledby 은 다른 요소의 ID 값을 매칭시킵니다.

aria-labelledby 은 Label 자체를 재정의하기 때문에 다른 모든 Label 속성들, aria-label 또는 HTML native label 과 함께 쓰여도 항상 aria-labelledby 을 우선합니다.


<label for="pw">Password:</label>
<input type="password" id="pw" aria-describedby="pw-help">
<div id="pw-help">비밀번호는 12 자 이상으로 이루어져야 합니다</div>

aria-describedbyaria-labelledby 와 같은 방식으로 다른 요소의 ID 를 매칭하여 현재 요소에 대한 설명을 나타냅니다.

둘의 사용법과 쓰임새가 굉장히 비슷하여 헷갈릴 수도 있지만 다른 목적으로 사용됩니다.

MDN Web Docs - Using the aria-describedby attribute 에는 다음과 같이 나와있습니다.

This is very similar to aria-labelledby: a label describes the essence of an object, while a description provides more information that the user might need.

label 관련 속성들과의 가장 큰 차이점은 Label 이 요소의 필수 설명이라면 aria-describedby 은 어디까지나 부연 설명이라는 점입니다.

그래서 위 예시 코드에서처럼 label 태그와 함께 사용할 수 있습니다.


2.3. aria-live

JavaScript 를 사용하면 페이지를 새로 로드하지 않고 일부만 동적으로 변경하는게 가능합니다.

동적인 변경은 페이지를 볼 수 있는 사용자들은 알 수 있지만, 시각 장애인들은 알아채기 어렵습니다.

그래서 이런 동적인 (실시간) 변경들을 알려줄 수 있는 aria-live 라는 속성이 제공됩니다.


<input type="text" name="email">
<div role="alert" aria-live="assertive">이메일 형식이 올바르지 않습니다.</div>

회원가입 또는 로그인 페이지에서 사용하는 Email Input 은 실시간으로 Email 형식 검사를 하여 올바른 형식이 아니면 빨간색 에러 메시지를 사용자에게 노출합니다.

페이지를 볼 수 있는 사용자는 그 정보를 실시간으로 확인할 수 있지만 시각장애인은 에러 메시지가 노출되었다는 사실을 모를 수 있습니다.

이럴 때 role="alert" 로 경고 역할을 갖고 있는 요소를 만들고 실시간 변화를 감지해서 알려주는 aria-live 속성을 추가할 수 있습니다.

aria-live 의 값으로는 다음 값들이 올 수 있습니다.

  • off (default)
  • polite : 현재 진행중인 음성 또는 타이핑 이후에 알림
  • assertive : 현재 진행중인 알림을 중단하고 즉시 알림

assertive 값은 사용자의 현재 작업을 방해할 수 있기 때문에 신중하게 적용해야 합니다.


2.4. role="alert"

alert 역할은 사용자에게 동적인 변화를 알려줄 때 사용합니다.

스크린 리더는 alert 역할이 붙은 요소가 업데이트 되면 바로 읽기 시작합니다.

role="alert" 로 설정한다는 건 aria-live="assertive" aria-atomic="true" 와 동일합니다.


2.5. role="timer"

현재 요소가 timer 로 사용되고 있다는 걸 의미합니다.

초를 계속 세주려면 aria-live 속성을 켜주면 되지만.. 1초마다 계속 컨텐츠가 갱신되기 때문에 알림이 부자연스럽게 계속 끊기는 이슈가 존재합니다.

만약 timer 에 초점이 잡혔을 때의 남은 시간만 읽어주길 바란다면 role="timer" 설정만 추가해주면 됩니다.


Reference

'공부 > Web' 카테고리의 다른 글

JWT (Json Web Token)  (3) 2021.05.22
URI 란?  (0) 2020.08.26

에러 로그

$ yarn start  
yarn run v1.22.17
$ react-scripts start
node:internal/modules/cjs/loader:488
      throw e;
      ^

Error [ERR_PACKAGE_PATH_NOT_EXPORTED]: Package subpath './lib/tokenize' is not defined by "exports" in /Users/Documents/my/projects/practice-codes/react-js-hello/node_modules/postcss-safe-parser/node_modules/postcss/package.json
    at new NodeError (node:internal/errors:371:5)
    at throwExportsNotFound (node:internal/modules/esm/resolve:416:9)
    at packageExportsResolve (node:internal/modules/esm/resolve:669:3)
    at resolveExports (node:internal/modules/cjs/loader:482:36)
    at Function.Module._findPath (node:internal/modules/cjs/loader:522:31)
    at Function.Module._resolveFilename (node:internal/modules/cjs/loader:919:27)
    at Function.Module._load (node:internal/modules/cjs/loader:778:27)
    at Module.require (node:internal/modules/cjs/loader:999:19)
    at require (node:internal/modules/cjs/helpers:102:18)
    at Object.<anonymous> (/Users/Documents/my/projects/practice-codes/react-js-hello/node_modules/postcss-safe-parser/lib/safe-parser.js:1:17) {
  code: 'ERR_PACKAGE_PATH_NOT_EXPORTED'
}

Node.js v17.0.1
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

원인

Mac OS 를 BigSur 로 업그레이드 후 연습중이던 React 프로젝트를 클론받아 실행했더니 에러가 발생했습니다.

복잡한 프로젝트도 아니어서 당황하던 중 검색해보니 node 17 버전을 사용해서 그렇다는 글을 봤습니다.

현재 제 노드 버전을 확인해보니 v17.0.1 을 사용하고 있었습니다.


해결

nvm (Node Version Manager) 을 설치한 후에 노드 버전을 다운그레이드 해서 해결했습니다.

처음에 brew install nvm 으로 설치했는데 잘 안되어서 nvm Github 을 보고 다시 설치했습니다.

# nvm 설치
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.0/install.sh | bash

설치 후에 ~/.zshrc 을 열어 맨 아래에 다음 내용을 붙여넣습니다.

기본 터미널을 bash 로 사용하고 있다면 ~/.bash_profile 에 추가하면 됩니다.

export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm

마지막으로 ~/.zshrc 를 갱신한 뒤 LTS 버전을 설치하고 다시 실행해보면 정상적으로 동작합니다.

# nvm command 가 안뜨면 갱신
$ source ~/.zshrc

# nvm 설치 확인
$ nvm -v
0.39.0

# node lts 버전 설치
$ nvm install --lts

# 16 버전이 설치됨
$ node -v
v16.13.0

Reference

1. Overview

Rails Session 은 우리가 알고있는 세션과 크게 다르지 않습니다.

사용자에 대한 일부 데이터를 저장하기 위해 사용합니다.

쿠키를 사용할 수도 있지만 외부에 노출되는 쿠키와 달리 서버에만 저장해야 하는 중요한 데이터들은 세션에 저장하면 편리합니다.


2. 사용법

Rails Session 은 Hash 처럼 사용하면 됩니다.

다른 Controller 에서 저장한 데이터를 꺼내 쓸 수 있습니다.


# app/controllers/index_controllers.rb
def create
    # ...
    session[:current_user_id] = @user.id
    # ...
end

예를 들어 IndexController 에서 저장한 세션값을..


# app/controllers/users_controllers.rb
def index
    # ...
    current_user = User.find_by_id(session[:current_user_id])
    # ...
end

UserController 에서 꺼내서 사용할 수 있습니다.


3. Session Stores

세션 저장소의 종류는 쿠키, 데이터베이스, Memcached, Redis 등등 다양합니다.

쿠키 세션 저장소를 제외한 모든 세션 저장소는 동일한 프로세스로 동작합니다.



3.1. Session 값 저장

  1. session[:current_user_id] = 1 을 호출했는데 기존에 사용하던 세션(Session ID) 이 없는 경우
  2. Rails 는 09497d46978bf6f32265fefb5cc52264 와 같은 임의의 Session ID 를 사용하여 sessions 테이블에 새로운 record 를 저장
  3. 해당 record 의 data 속성에 {current_user_id: 1} (Base64-encoded) 값도 함께 저장
  4. 생성한 Session ID (09497d46978bf6f32265fefb5cc52264) 는 Set-Cookie 를 사용하여 브라우저에게 전달

3.2. Session 값 가져오기

  1. 브라우저가 서버에 요청할 때 Cookie: 헤더를 사용해서 동일한 쿠키값을 전달
    • (1번 예시) Cookie: _my_app_session=09497d46978bf6f32265fefb5cc52264; path=/; HttpOnly
  2. 코드에서 session[:current_user_id] 을 호출
  3. 쿠키에 있는 Session ID 값으로 sessions 테이블에 있는 record 를 가져옴
  4. record 에 있는 data 속성에서 current_user_id 값을 가져옴

Reference

+ Recent posts