728x90

18.04 기분으로 작성한 포스팅입니다.

 

ubuntu를 처음 세팅하고나면 root계정의 비밀번호를 먼저 설정해줍니다.

sudo passwd root

 

ssh 설정을 해줄건데요. 딱 3줄만 수정해주면됩니다.

nano /etc/ssh/sshd_config

 

파일을 편집을해볼까요?

PermitRootLogin yes
PasswordAuthentication yes
ChallengeResponseAuthentication no

 

다른 설정은 건드리지말고 위의 3줄만 위와같이 잡아주면 됩니다.

 

잘 설정됐는지 테스트 및 재실행을 해볼게요

# test
sudo sshd -t

# restart
sudo service sshd restart

 

재실행이 잘 됐다면, 터미널을 종료하지말고 새로운 터미널을 열어 접속을 테스트 해봅니다.

# 설정 전 접속방법
ssh -i 'key.pem' ubuntu@ip(혹은 domain)

# 설정 후 접속방법
ssh root@ip(혹은 domain)

 

보안적인 면에서는 늘 키파일로 로그인하는게 좋겠지만, 여러 상황에 따라 비밀번호 접속 설정이 필요할수 있습니다.

728x90
반응형
728x90

개발을 하다보면 사용자가 페이지에서 이탈하는 시점에 이벤트를 발생시켜야 하는 경우가 있다.

예를들어, 페이지 이탈전 저장알림을 띄운다거나 페이지에 머무는 시간을 체크하거나 할때 말이다.

 

이때 필요한게 이벤트 리스너 중에 'beforeunload'라는 것이다.

MDN 문서에 보면 beforeunload에 대해 다음과 같이 설명되어있다.

beforeunload 이벤트는 문서와 그 리소스가 언로드 되기 직전에 window에서 발생합니다. 
이벤트 발생 시점엔 문서를 아직 볼 수 있으며 이벤트도 취소 가능합니다.

 

MDN도 예시로 아래와 같은것을 들고있다.

beforeunload 이벤트를 사용하면 사용자가 페이지를 떠날 때 정말로 떠날 것인지 묻는 확인 대화 상자를 표시할 수 있습니다. 
사용자가 확인을 누를 경우 브라우저는 새로운 페이지로 탐색하고, 취소할 경우 탐색을 취소하고 현재 페이지에 머무릅니다.

 

자, 이 리스너가 무엇인지  언제필요한건지 알았으니 어떻게 쓰는지도 알아보자

window.addEventListener('beforeunload', onBeforeClosed, false);

const onBeforeClosed = (e) => {
  // 표준에 따라 기본 동작 방지
  event.preventDefault();
  // Chrome에서는 returnValue 설정이 필요함
  event.returnValue = '';
}

 

MDN 예시를 약간 변형해보았다. 브라우저 호환성을 꼭 참고해서 사용하도록 하자.

여기서 중요한 포인트가 있다.

 

예상하거나 의도하지 않은 동작이 일어나지 않게 하기위해서는

이벤트 리스너를 다시 제거해주는 작업도 진행해야한다.

window.removeEventListener("beforeunload", onBeforeClosed, false);

 

 

728x90
반응형
728x90

사용자기반 앱/웹 개발을 하다보면 Input은 안쓸수가없다고해도 과언이 아니다.

 

이때, 모바일 입력 편의성 제공을 위해 Input타입을 number로 잡아줬더니

 

숫자롤러가 나타나 디자인을 해치는 일이 발생한 경험이 있을것이다.

 

간단한 css추가로 숫자롤러를 없애보자.

 

input::-webkit-outer-spin-button,
input::-webkit-inner-spin-button {
    /* display: none; <- Crashes Chrome on hover */
    -webkit-appearance: none;
    margin: 0; /* <-- Apparently some margin are still there even though it's hidden */
}

input[type=number] {
    -moz-appearance:textfield; /* Firefox */
}

 

 

728x90
반응형
728x90

해당 포스팅은 Gsuite에 기반한 포스팅임을 미리 말씀드립니다.

 

광고x

 

 

오랜만에 정성가득 포스팅을 해보고자 합니다.

 

내 도메인으로 이메일을 등록하는 것부터

 

여러가지 상황으로 인해 내 도메인의 네임서버(NS, name server)를 변경했을때 이메일이 먹통이 됐다면..?! 에 대한 해결방안까지.

 

 

 

ㄱ. 도메인 구매

 

국내에 유명한 호스팅업체가 몇 군데 있습니다.

예를들어 Cafe24, 가비아, 후이즈, 호스팅케이알 등

 

개인적으로 가비아나를 주로 이용하고 호스팅케이알이 저렴하다고 생각해 종종 이용합니다.

어디든 상관없으니 들어가서 마음에 드는 도메인을 검색해 구매하도록 합니다.

 

 

 

 

ㄴ. Gsuite 시작

 

구매하였다면, 이제 Gsuite를 시작할 차례입니다.

workspace.google.co.kr/intl/ko/

 

위의 링크로 들어가면 우측상단에 "무료 평가판 시작" 이라는 버튼이 있습니다.

해당 버튼을 눌러 봅니다.

그러면 간단한 설정들이 몇가지 나오는데요.

차근차근 진행해주면 됩니다. (직원수, 국가, 연락처 등이니 부담없이 편하게 하시면 됩니다.)

 

자, 기본정보를 입력하고나면 도메인을 소유하고있냐고 묻는데요.

우리는 구매한 도메인이 있으니, 도메인을 소유하고 있습니다를 클릭합니다.

 

그리고 구매한 도메인을 입력해줍니다.
도메인이 tistory.com(www.tistory.com)이라면 tistory.com을 입력해주면 되겠죠?

 
자, 여기부터가 조금 어려울수있는데요 .

무슨무슨 설정을 하라는 가이드가 나옵니다. 방황하지 않도록 합니다.

 

 

 

 

ㄷ. 메일서버 설정


도메인을 구매했던 사이트로 돌아가

마이서비스(마이페이지) > 도메인 관리툴
로 이동합니다.

잘 살펴보면 어드 사이트던간에 도메인 관리툴에 "DNS 설정" 혹은 "DNS 레코드 설정"등의 버튼이 있습니다.
해당 버튼을 클릭해 관리 화면으로 이동해봅니다.

설정된게 아무것도 없을텐대요. 여기서 구글에서 가이드해준대로 설정을 하나씩 진행해줄 예정입니다.
1. "추가"버튼을 클릭
2. primary에 구글에서 지정해준 1,5,10, 15 등을 입력
3. value/description에 구글에서 지정해준 ASPMX.L.GOOGLE.COM. 등의 값을 입력
4. 총 5-6개정도가 가이드 됩니다. 꼼꼼하게 입력했는지 다시한번 확인합니다.
5. TTL (수명)은 기본값/공백으로 두어도 무방합니다.
6. MX 확인 레코드(mx-verification.google.com으로 끝남 <- 이것도 빼먹지 않았는지 한번 더 체크합니다.

 

반드시 설정을 저장하고 화면에서 나오도록 합니다.

 


ㄹ. Gsuite에서 연결 확인

 

여기까지 했다면 설정은 거의 다 끝났습니다.
다시 Gsuite로 돌아가서 다음/확인/완료 버튼을 눌러주면 Guiste연결을 체크합니다.
다만, 위의 설정을 저장하고나면 처리에 어느정도 시간이 소요될 수 있어 연결오류가 뜰수도 있습니다.

당황하지 말고 한번더 연결확인을 해주면됩니다.

설정은 모두 끝났고

test email보내기 기능을 통해 이메일을 발송, 해당 계정에서 수신해보면 됩니다.

 

끝!




ㅁ. 기타 이슈 해결 - 네임서버를 변경했더니 이메일이 수신되지 않아요. 

설정을 모두 마치고 잘 이용하다가
어느날 도메인 만료 혹은 호스팅 이전 등의 상황으로 인해 네임서버를 변경해야 될일이 생길 수 있습니다.
사람일은 모르는법이니까요.

그러면, 이메일이 정상적으로 동작하지 않게 되는데요.
이는, ㄹ 단계에서 연결설정 했을때와 내 도메인의 상태가 달라졌으니 사실 당연한 결과입니다.

너무 걱정하지 않아도됩니다. 걱정은 제가 미리했으니까요.

네임서버를 변경해서 도메인이 바라보게한 호스팅 서버가 있죠?
그곳에서 'ㄷ' 단계에 해당하는 설정을 해주면됩니다.


그리고, 마지막으로...
Gsuite 어드민 > 도메인 관리 > 설정
에 들어가보면 이메일 활성화 하라는 빨강색 글씨가 있을겁니다.

도움말 > support.google.com/a/answer/7009324?hl=ko&ref_topic=6302293#zippy=%2C%EB%8B%A8%EA%B3%84-%EC%83%88-%EB%8F%84%EB%A9%94%EC%9D%B8-%EC%B6%94%EA%B0%80-%EB%B0%8F-%EC%9D%B4%EB%A9%94%EC%9D%BC-%EC%84%A4%EC%A0%95%ED%95%98%EA%B8%B0

그 버튼을 꾹 눌러주면 구글에서 다시 체크를 진행하고 정상적으로 설정되어있을경우 다시 메일시스템을 복구해줍니다.

 

긴  글 읽어주셔서 감사합니다.

728x90
반응형
728x90

보통 "반응형", "모바일 사이트" 하면 나오는

 

키워드 두가지가 있다

 

Viewport와 Media 쿼리

 

 

 

둘 중 누구도 없어서는 안되면 필수적인 요소다.

 

media 쿼리는 웹 프론트를 만져본 사람이라면 기본적인 사용법은 익히들 알고있다.

 

물론 이것도 공부하려면 할것들이 제법 많지만

 

필요할때 구글링하면서 쓰면 될만한 내용이라

 

미리부터 공부할 필요는 없을것 같다

 

 

 

그런데 Viewport가 오늘 신선한 충격으로 다가왔다.

 

<meta name='viewport' content='width=device-width, initial-width=1.0' />

그냥 헤더에 이거 한 줄 넣는것.

 

이 이상으로 이것이 무슨 의미를 갖는지 정확히 어떤 역할을 하는지 관심갖지 못했었다.

 

그냥 넣으면 대부분 만사 오케이니까

 

더이상 건드릴 이유도 공부할 이유도 없었다. (몇가지 옵션 정도는 쓰고있지만..)

 

 

 

그런데 재밌는 부분이 있다.

 

내 데스크탑 기준의 css를 태블릿에서도 비율에 맞게 유지하려면 어떻게 하면좋을까?

 

media query로 일일히 퍼센트에 맞게 조정해준다?

 

물론 이거도 노력하면 비슷한 효과를 볼수있다.

 

지금 내가 말하려고 하는것은 물리적/논리적 해상도의 차이에 대해서다.

 

(지금부터, 아래의 내용은 다소 비 전문적이거나 부정확할 수 있습니다.)

 

 

 

 

물리적은 실제 디바이스의 너비이고

 

논리적은 제품사양에 명시된 스펙에 나온 해상도라고 생각하면 될것같다...

 

 

 

자, 먼저 Viewport를 제외하고 생각하면 media 쿼리는 물리적 해상도를 기준으로 

 

컴포넌트/태그 들의 크기를 일정하게 조정해 주는 역할을 한다고 보면 될것같다

 

 

그런데, Viewport를 잘 이용하면 특정 값을 기준으로 논리적 해상도를 기준으로 css를 적용할수있다. (내가 오늘 이해한 바로는)

 

간단히는 이렇게 볼수있다.

 

<meta name='viewport' content='width=1190' />

이렇게 설정해둔다면

 

디바이스의 크기에 상관없이 

 

css의 width: 100%를 1190px로 보게한다.

 

이미 작성해둔 css들은 걱정하지 않아도, 이를 기준으로해서 알맞게 조정한 크기를 보여주게된다.

 

viewport는 정말 어마무시한 친구다.

 

위의 설정을 누가 이용하는지 아는가?

 

바로, 우리가 매일 보고 사용하고 있는 "네이버"다

 

 

 

네이버를 데스크탑에서 열고 개발자 도구를 보면 viewport가 위와 같이 설정되어있다.

 

그런데, 화면의 크기를 모바일 사이즈정도로 작게하고

 

화면을 다시 로드하면 m.naver.com으로 리디렉션하며 

 

해당 페이지에서 개발자 도구를 보면 viewport는

 

<meta name='viewport' content='width=device-width, initial-width=1.0' />

과 같은 우리가 흔히 아는 내용으로 작성되어있다.

 

정말 놀라운 viewport의 세계다.

 

 

 

 

현재 내가 운영중인 서비스에서는 네이버처럼 m.을 분리하여 운영할 여력은 없어서

 

javascript를 이용해 디바이스 크기에 따라 로드 시점에 viewport를 다르게 주도록 설정해보았다.

728x90
반응형
728x90

늘 하듯 AWS, GCP등을 다루다가

 

오랜만에 웹호스팅 문의를 받게되었다.

 

근데 이게 웬걸..? 오랜만에 하려니 어떻게 하면 좋을지 감을 잃었다.

 

솔직히 벙쪘다.

 

 

 

주어진건 가비아 계정과 구매된 도메인

 

요청사항은 HTML파일을 줄테니 호스팅 해달라는것.

 

호스팅 비용은 무료로.

 

 

 

어떻게 해야할까

 

내가 어떻게 했었을까

 

도메인은 원래 있던 사이트에서 어떻게 호스팅 쪽으로 가져올것인가\

 

SSL은 어떻게 붙이지?

 

 

 

늘 하던 작업들이지만, AWS, GCP등 클라우드 서버를 잃은 나는 무언가 무기력했다.

 

어떻게 위 작업을 해낼수 있을것인가...

 

고민끝에 떠올린 방법은

 

Netlify로 호스팅하고 가비아에서 도메인의 네임서버를 Netlify 네임서버로 변경해주는것

 

 

 

먼저, 건네받은 Html이 포함된 폴더를 통째로 Netlify에 올렸다.

 

그리고  차례로 절차를 밟아나갔다.

 

Netlify의 네임서버 사용.

 

그리고 Netlify의 1~4 네임서버를 복사해 가비아의 도메인 네임서버에 덮어씌우기

 

 

 

그렇게 약 6시간이 지난후 정상적으로 호스팅에 성공했다.

 

* 참고로 도메인은 무료가 아닙니다. 

728x90
반응형
728x90

React기반인 서비스를 운영중인 회사의 서비스 페이지에 Fullpage.js + aos를 적용해보았습니다.

아주 쉽고 빠르게 잘 붙었지만

이게 웬걸...

 

로딩만 했다하면 도저히 원인 파악이 어려울 정도의 상황이 발생했습니다.

스크롤 위치가 이상한데 잡히면서 컴포넌트들이 나타나지 않았습니다.

 

aos가 나타나는 trigger에 문제가 있다고 짐작할뿐 대체 왜 이런 현상이 계속되는가...

를 3시간을 추적에 추적을했습니다.

 

그러다 Lazy loading이라는 키워드를 발견했고

서비스페이지만 Lazy loading을 제거했습니다.

 

와우 너무나도 정상적으로 동작..?!

 

이렇게 간단한 이슈인것을 몇시간을 헤맸네요.

Lazy loading을 했을때 왜 해당 이슈가 발생하는지 근본적인 원인에 대해 탐구하는 포스팅을 다음 글에서 이어가도록 하겠습니다.

 

728x90
반응형
728x90

mongo를 쓰다보면,

list내에서 특정 값 혹은 document를 제거해야되는 경우가 굉장히많다.

 

이때는, 원본 자료를 불러와 해당 값을 제거한뒤 업데이트 시키는 방법 (덮어씌우기)을 주로 이용했다.

 

그런데, 자료를 불러올 필요가 없는 상황에서 해당 값만 바로 제거할수는 없을까? 하는 생각이 들었다.

찾아보니, 이때는  $pull 키워드로 해당 작업이 가능하다.

늘상 써오던 $set과 크게 다르지 않다

 

간단한 예제와 함께 포스팅을 마무리하기겠다.

// collection 이름이 Orders인 경우

Orders = [ 
  {
    _id: ...,
    menus: ['coffee', 'tea'],
  },
  {
    _id: ...,
    menus: ['bread', 'tea'],
  },
  {
    _id: ObjectId('abc'),
    menus: ['coffee', 'fruit'],
  },
];

// 특정 대상에 대한 처리 - ObjectId('abc')의 fruit을 제거할때
db.Orders.findOneAndUpdate({ _id: ObjectId('abc') }, { $pull: { menus: { $in: ['fruit']} }  })

// 전체 대상에서 fruit을 제거
db.Orders.updateMany({}, { $pull: { menus: { $in: ['fruit'] } } })


menu가 document형태로 들어가있을 경우?
Orders = [ 
  {
    _id: ...,
    menus: [
      {
        _id: ...,
        name: 'coffee',
        price: 100,
      },
      {
        _id: ...,
        name: 'tea',
        price: 200,
      }
    ],
  },
  {
    _id: ...,
    menus: [
      {
        _id: ...,
        name: 'bread',
        price: 150,
      },
      {
        _id: ...,
        name: 'tea',
        price: 200,
      }
    ],
  },
  {
    _id: ObjectId('abc'),
    menus: [
      {
        _id: ...,
        name: 'coffee',
        price: 100,
      },
      {
        _id: ...,
        name: 'fruit',
        price: 500,
      }
    ],
  },
];

// 특정 대상에 대한 처리 - ObjectId('abc')의 fruit을 제거할때
db.Orders.findOneAndUpdate({ _id: ObjectId('abc') }, { $pull: { menus: { name: 'fruit' } }  })

// 전체 대상에서 fruit을 제거
db.Orders.updateMany({}, { $pull: { menus: { name: 'fruit' } } })

 

 

[참고자료]

docs.mongodb.com/manual/reference/operator/update/pull/

728x90
반응형
728x90

오랜만에 포스팅을 합니다.

 

운영중인 프로그램이 대규모 업데이트가 연속적으로 이루어지고있어

 

포스팅할 시간이 없네요.

 

대규모 업데이트를 하다본니, 이것만큼은 꼭 공유해주고 싶다! 라고 생각이 든 파트가 있어 포스팅하게되었습니다.

 

바로 로깅 시스템인데요.

 

 

다양한 라이브러리에서 로깅시스템을 지원해주지만,

내가 만들어 자유자재로 쓰는 가벼운 로깅시스템은 디버깅에서 최고의 효율은 발휘해주는것 같습니다.

 

예를들어, try/catch구문에서  해당 코드의 디렉토리 위치부터 에러발생 시간, 에러 내용을 모두 표시해준다면
에러복구에 정말 유용하겠죠?

따라서, fs시스템을 이용해 간단한 로깅메서드를 만들어 해당 시스템을 구축해 보도록 하겠습니다.

 

# 순서

1. 로깅 메서드 만들기

2. 활용 예제

3. 응용 예제

 

 


 

1. 로깅 메서드 만들기

const fs = require('fs');
const moment = require('moment');

export const logging = log => {
  const today = moment().format('YYYYMMDD');
  const date = moment().format('YYYY.MM.DD HH:mm:ss');

  const file = `./log/${today}_server.log`;
  const newLine = `[${date}] - ${log}`;

  fs.readFile(file, (err, data) => {
    if (err) {
      return console.error('read log err: ', err);
    }
    
    fs.writeFile(file, data + '\n' + newLine, err => {
      if (err) {
        return console.error('write log err: ', err);
      }
    });
  });
  
};

# 예제 키워드: fs, moment, 백틱

파일을 읽어오고 줄을 바꿔 내용을 덧붙이는 예제입니다.
writeFile은 해당 파일이 존재하지 않으면 기본적으로 create & write하게 되어있습니다.

 

(추가적으로, 해당 옵션은 flag옵션으로 변경가능합니다.)


2. 활용 예제

const { logging } = require('./util');

try {
  // something ...
  
} catch(error) {
  console.log('check system error log: ', error); // 콘솔에서 에러 확인 (개발에 활용)
  logging(error); // 로그파일에 기록 (나중에 확인하기 위함)
  return 'error;
}

 

우리의 logging 예제코드가 util파일에 있다고 가정했을때의 예제입니다.

이렇게, logging(error) 한줄 추가해주는것만으로

" [날짜] - 에러내용 "

 

구조의 에러 리포트를 누적해둘수있습니다.

예외처리에 빠짐없이 잘 써둔다면 디버깅에서 아주 유용하게 쓰일것으로 보입니다.

 

좀더 실용적이고 쓸만한 메서드로 개선해볼까요

 

 

3. 응용예제

# util
const fs = require('fs');
const moment = require('moment');

export const logging = (log, dir) => {
  const today = moment().format('YYYYMMDD');
  const date = moment().format('YYYY.MM.DD HH:mm:ss');

  const file = `./log/${today}_server.log`;
  const newLine = `[${date}] - ${log} ${ dir ? '\n'+ 'directory: ' + dir : ''}`;

  fs.readFile(file, (err, data) => {
    if (err) {
      return console.error('read log err: ', err);
    }
    
    fs.writeFile(file, data + '\n' + newLine, err => {
      if (err) {
        return console.error('write log err: ', err);
      }
    });
  });
  
};


# server
const { logging } = require('./util');

try {
  // something ...
  
} catch(error) {
  console.log('check system error log: ', error); // 콘솔에서 에러 확인 (개발에 활용)
  logging(error, __filename); // 로그파일에 기록 (나중에 확인하기 위함)
  return 'error;
}

# 예제 키워드: __filename

 

 

이렇게 간단한 수정만으로 파일의 디렉토리까지 남겨가며 모든 에러를 추적하게됩니다.

 

728x90
반응형
728x90

모바일 반응형의 기본인 viewport메타태그

 

<meta name="viewport" content="width=device-width, initial-scale=1.0, minimum-scale=1.0, user-scalable=yes" />

maximum-scale을 포함해 다양한 옵션이 존재한다.

 

이중에서 user-scaleable이 yes 상태이면

input창을 focus했을떄 자동으로 화면이 줌인된다.

user-scalable=0 으로 설정하면 해당 이슈는 해결된다

 

다양한 사용자들에게 접근성을 제공하는 옵션이기도하니 서비스의 특성에 맞게 설정하도록하자.

728x90
반응형