Skip to main content

Linux startup 파일에서 피해야할 것

· 2 min read

스타트업 파일은 사용자가 로그인 할 때 시스템이 어떻게 반응해야 하는지를 결정한다. 스타트업 파일 수정시에 다음 사항들을 꼭 피해야한다.

주의

  • 셸 스타트업 파일에 그래픽 명령을 넣지 않는다.
  • 셸 스타트업 파일에 DISPLAY 환경 변수를 설정하지 않는다.
  • 셸 스타트업 파일에 터미널 유형을 설정하지 않는다.
  • 스타트업 파일에서 표준 출력으로 인쇄하는 명령을 실행하지 않는다.
  • 셸 스타트업 파일에 LD_LIBRARY_PATH를 결코 설정하지 않는다.

LD_LIBRARY_PATH 변수 조작시 런타임 링커가 모든 프로그램에 대해 이 디렉터리들을 찾기 때문에 충돌을 일으킬 수 있고, 라이브러리의 조합이 틀어질 수 있기 때문이다.

추가적으로 디폴트 스타트업 파일에 상세한 주석을 충분히 첨부한다.

Laravel 5.5 - Model Event Listener

· 3 min read

라라벨 이벤트 리스너 기능을 붙혀보자. Model이 Create 될 때 이벤트 리스너를 붙혀 다른 기능을 연결하는 예제가 가장 쉽다. (예를 들면 로그가 생성될 때 SMS를 날리는 경우)

이벤트 생성

EventServiceProvider

먼저 EventServiceProvider에 내가 사용할 이벤트와 리스너를 등록해줘야한다.

app/Providers/EventServiceProvider
<?php
...
class EventServiceProvider extends ServiceProvider
{
protected $listen = [
// 로그 생성시 이벤트를
'App\Events\LogCreated' => [
// 로그 생성됨 리스너에 연결시켜준다.
'App\Listeners\LogCreatedListener',
],
];

$listen 변수에 기본으로 등록되어있는 이벤트는 지워주자

generate

이제 소스 파일을 생성시켜준다.

php artisan event:generate

명령어를 실행하면 app/Eventsapp/Listeners에 방금 등록한 이벤트 리스너 파일이 자동으로 생성된다.

바인딩

모델

모델에서 방금 추가된 이벤트를 연결시켜주자.

app/Models/Log
<?php
use App\Events\LogCreated;

class Log extends Model
{
...
protected $dispatchesEvents = [
// 모델이 create(insert) 되면 해당 이벤트를 호출한다.
'created' => LogCreated::class
// use 구문을 사용하지 않고 여기에 직접 "App\Events\LogCreated" 로 정의해도 될 것 같은데 테스트는 안 해봤다.
];
}

이벤트

이벤트에서 해당 모델을 연결시켜주자.

app/Events/LogCreated
<?php
use App\Models\Log;
...

class LogCreated
{
use Dispatchable, InteractsWithSockets, SerializesModels;

// 리스너에서 받을 모델 변수를 public으로 생성한다.
public $log;

// DI
public function __construct(Log $log)
{
$this->log = $log;
}

public function broadcastOn()
{
// 채널을 이용하지 않을 것이기에 빈 배열을 리턴시킨다.
return [];
}
}

처리

리스너에서 받은 이벤트를 처리하자.

app/Listeners/LogCreatedListener
<?php
...
use App\Events\LogCreated;

class LogCreatedListener
{
...
public function handle(LogCreated $event)
{
// Events의 public으로 선언한 데이터가 $event 아래로 바인딩 된다.

$log = $event->log;
logger('LOG Received');
logger($log);

// 여기서 기능을 구현하면 된다.
}
}

ShouldQueue로 확장해 큐에 담을 수도 있다.

여담

메일 발송과 비슷한 플로우였다.

Vue에서 jquery와 bootstrap 전역으로 사용하기

· One min read

expose-loader의 설치가 필요 없는 방법을 사용해보자

Vuejs-kr에 좋은 내용이 있지만 웹팩을 통해 jquery를 꺼내는 방법이 더 간단하다.

설치

yarn add jquery bootstrap

설정

webpack

build/webpack.base.conf.js
const webpack = require("webpack");

module.exports = {
plugins: [
new webpack.ProvidePlugin({
$: "jquery",
jquery: "jquery",
"window.jQuery": "jquery",
jQuery: "jquery",
}),
],
};

eslint

.enlintrs.js
module.exports = {
globals: {
$: true,
jQuery: true,
},
};

연동

src/main.js
import "bootstrap";

new Vue({});

Top 명령어 단축키

· One min read

top 명령어는 자주 치는 명령어지만 예쁘게 소팅하기란 쉽지 않다.

단축키

단축키내용
spacebar화면 즉시 업데이트
M현재 상주 메모리 사용량에 따라 분류
T전체 누적 CPU 사용량에 따라 분류
P현재 CPU 사용량(디폴트 값)에 따라 분류
c프로세스 경로 표시
u오로지 한 사용자의 프로세스만 보여줌
f다른 종류의 통계 자료가 나타나도록 선택
?모든 top 명령에 대한 사용법의 개요를 보여준다.

/etc/passwd 파일 구조

· One min read

구조

로그인명:비빌번호:사용자 ID:그룹 ID:실제 유저명:홈 디렉토리:쉘

  • 예시: root0:0:superuser:/root:/bin/bash
  • 주석이나 공백은 허용되지 않는다.

비밀번호 필드

  • x 표시: 암호화된 비밀번호가 /etc/shadow 파일에 저장되어 있다는 것
  • * 표시: 로그인할 수 없는 사용자
  • 공백: 로그인시 비밀번호가 필요 없음

비밀번호 변경

  • passwd 명령어는 누구나 알지만
  • vipw 로 /etc/passwd 파일을 통째로 편집 가능

Redis Flush가 안 될 경우 전체 캐시 비우기

· One min read

FLUSH 명령어는 보안상 empty 값으로 대체되어있는 경우가 많은데, 이 경우 전체를 비우는 명령어가 없어서 편법을 써야한다.

해결

redis-cli -a '비밀번호' KEYS "*" | xargs redis-cli -a '비밀번호' DEL

이 명령은 모든 키 리스트를 가져와 하나씩 지워준다.

Vue multi page app에서 코드가 미리보이는 현상 제거

· One min read

뷰에 데이터가 바인딩 되기전 {{태그}} 구문이 보일 때 다음과 같이 하면 된다.

해결

v-cloak api에 자세하게 나와있다.

<!-- vue가 바인딩 될 영역에 v-cloak attribute를 추가한다 -->
<div id="vue_area" v-cloak></div>
[v-cloak] {
display: none;
}

더 멋진 방법

로딩시에 content 영역에 loading...이라는 문구를 찍어주는 방법으로 여기에 자세히 설명되어있다.

[v-cloak] > * {
display: none;
}
[v-cloak]::before {
content: "loading...";
}

Laravel 5.5에 JWT (Json Web Token) Auth 추가하기

· 3 min read

검색해 나온 포스트들은 5.4버전에 대해서만 나와있어서, 5.5에서는 아무짝에 쓸모가 없었다. 라라벨에서 좃인증을 시작해보자.

설치

jwt-auth

171103 기준으로 dev-develop 버전의 패키지를 설치해야한다.

composer require tymon/jwt-auth:1.0.0-rc.1

service provider 등록

config/app.php
<?php

'providers' => [
...
Tymon\JWTAuth\Providers\LaravelServiceProvider::class,
],

'alias' => [
...
'JWTAuth' => Tymon\JWTAuth\Facades\JWTAuth::class
],

설정파일 publish

php artisan vendor:publish --provider="Tymon\JWTAuth\Providers\LaravelServiceProvider" --force

secret key 생성

php artisan jwt:secret

연동

API Route 설정

API 가드와 유저 모델을 설정하다.

config/auth.php
<?php
return [
'defaults' => [
'guard' => 'api', // 기본 가드를 api로 변경
'passwords' => 'users',
],

'guards' => [
...
'api' => [
'driver' => 'jwt', // api 가드를 jwt 인증을 사용
'provider' => 'users',
],
],

'providers' => [
'users' => [
'driver' => 'eloquent',
'model' => App\Models\Member::class, // 유저 모델을 해당 모델로 변경
],
],
...
];

Member Model 설정

app/Models/Member.php
<?php
...
// jwt를 모델에서 사용하기 위해 추가한다.
use Illuminate\Foundation\Auth\User as Authenticatable;
use Tymon\JWTAuth\Contracts\JWTSubject;
...

class Member extends Authenticatable implements JWTSubject
{
// 아래 두 메소드가 구현되어야 실행된다.
public function getJWTIdentifier() {
return $this->getKey();
}

public function getJWTCustomClaims() {
return [];
}
}

사용하기

login

app/Http/MemberController.php
<?php
public function login(Request $request) {
$credentials = $this->validate($request, [
'id' => 'required|string',
'password' => 'required|string'
]);

if ($token = $this->guard()->attempt($credentials)) {
return $this->respondWithToken($token);
}

return response()->json(['message' => 'Unauthorized'], 401);
}

protected function respondWithToken($token) {
return response()->json([
'access_token' => $token,
'token_type' => 'bearer',
'expires_in' => $this->guard()->factory()->getTTL() * 60
]);
}

public function guard() {
return Auth::guard();
}

Authorized Routes

Accept Header

application/json 로 설정해야 오류가 예쁘게 반환된다.

token 태우기

## header 이용한 방법
Authorization: Bearer yourtokens...

## Querystring으로도 인증 가능
https://gracefullight.github.io/me?token=yourtokens...

routes

routes/api.php
Route::group(['middleware' => 'auth:api'], function() {
Route::get('member/logout', 'MemberController@logout');
Route::get('member/me', 'MemberController@me');
});

logout

app/Http/MemberController.php
<?php
public function logout(Request $request) {
$this->guard()->logout();
return response(null, 204);
}

refresh

refresh는 auth:api 미들웨어 없이 처리되어야한다.

app/Http/MemberController.php
<?php
public function refresh() {
return $this->respondWithToken($this->guard()->refresh());
}

여담

Expired거나 Unauthoriezed경우 status code로 체크하면 된다. 5.5버전 메뉴얼이 부족하다.