1. pg_dump 가 어디에 있는지 확인

 

# find / -name pg_dump
/usr/pgsql-11/bin/pg_dump
/usr/bin/pg_dump

 

2. postgres 11버전을 사용하기에

 

# /usr/pgsql-11/bin/pg_dump -U [user] -Fp -f [filename] [dbname] -t [tablename]

 

 

3. 다시 복구 할때  

# su - postgres

4. 덤프파일이 있는 곳으로 이동후

# psql -f [filename] [dbname]

In my case..

 

회사에서 서비스하고 있는 앱에 수정 사항이 생겨서 기능을 추가하여 오랜만에 플레이 스토어에 등록했는데 안전하지 않은 TrustManager 구현 ~~ 어쩌고저쩌고.. 오류로 심사 거부가 발생했다.

 

앱 전문으로 개발하는게 아니라.. 당황했지만  developers.android.com 힘을 빌어 'HTTPS 및 SSL을 사용한 보안' 으로 검색하면 가이드가 나온다.

 

일단 회사에서는 잘알려진 발급기관의 인즈서CA를 쓰고 있기 때문에 가이드에 있는데로 구현하고, 일부는 구글링 해서 간단하게 래핑 클래스를 작성하였다.

 

/**
 * The Class HttpHelper.
 * Http 핼퍼 클래스
 */
public class HttpHelper2 {

	/**
	 * Instantiates a new http helper.
	 */
	public HttpHelper2() {
	}

	public String requestHttp(String sUrl, Map<String, Object> params) throws IOException {
		URL url = new URL(sUrl);
		StringBuilder postdata = new StringBuilder();
		for (Map.Entry<String, Object> param : params.entrySet()) {
			if(postdata.length() != 0) postdata.append('&');
			postdata.append(URLEncoder.encode(param.getKey(), "UTF-8"));
			postdata.append('=');
			postdata.append(URLEncoder.encode(String.valueOf(param.getValue()), "UTF-8"));
		}
		byte[] postDataBytes = postdata.toString().getBytes("UTF-8");

		HttpURLConnection conn = (HttpURLConnection)url.openConnection();
		conn.setRequestMethod("POST");
		conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
		conn.setRequestProperty("Content-Length", String.valueOf(postDataBytes.length));
		conn.setDoOutput(true);
		conn.getOutputStream().write(postDataBytes);
		BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));

		StringBuilder sbRecv = new StringBuilder();
		String sLine;
		while((sLine = in.readLine()) != null) {
			System.out.println(sLine);
			sbRecv.append(sLine);
		}
		in.close();
		return sbRecv.toString();
	}
}

//가져다 쓸때
public class RegistTask extends AsyncTask<String, Integer, String> {
  protected String doInBackground(String... params) {
    String url = "https://yoursite";

    HttpHelper2 httpHelper = new HttpHelper2();
    Map<String, Object> postdata = new LinkedHashMap<>();
    postdata.put("tag", CommonPreference.TAG_REGISTER);
    postdata.put("email", email);
    postdata.put("regid", regid);
    postdata.put("mobile_desc", model);
    try {
    recvbuf = httpHelper.requestHttp(url, postdata);
    } catch (IOException e) {
    e.printStackTrace();
    }
  	return recvbuf;       
  }
}

<Menu.Item key="1">

<Link to="/" onClick={this.menuClick("Home")}/>

<span>Home</span>

</Menu.Item>

 

onclick 함수 사용시 다음과 같은 에러를 만날때.

 

Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops

 

# 인자가 있는 경우에

 

<Link to="/" onClick={() => this.menuClick("Home")}/>

 

# 인자를 사용하지 않는 경우

 

<Link to="/" onClick={this.menuClick}/>

isinstance 함수 사용

 

##! @brief 어떤 함수 
def some_fn(self, marketl)
    # 항상 스트링 변수가 넘어오는줄 알았는데 tuple 타입으로 넘어오는 경우가 발생
    if isinstance(market, tuple):
        market = market[0]
        # som process ~~~~

***.py 에서 html 템플릿으로 딕셔너리 데이타를 넘길때,  혹은 json 형태로 데이터 셋을 받았을때,

 

html 태그를 인식하게 해서 웹화면에 표현하고자 한다면 오토이스케이프를 쓰자.

 

{% autoescape off %}
   {% for key, val in conds.items %}

      ~~~ 내용 ~~~
   {% endfor %}
{% endautoescape %}

import multiprocessing as mp
import time
from time import sleep


def foo(q, id):
    while q.empty() == False:
        print ("%s %s" % (id, q.get()))

        for i in range(1, 5):
            sleep(0.1)

        sleep(0.01)

if __name__ == '__main__':
    mp.set_start_method('spawn')
    q = mp.Queue()
    stime = time.time()
    q.put('001')
    q.put('002')
    q.put('003')
    q.put('004')
    q.put('005')
    q.put('006')
    q.put('007')
    q.put('008')
    q.put('009')
    q.put('010')

    MAX_PROCESS = 20
    p = None
    for i in range (0, MAX_PROCESS):
        p = mp.Process(target=foo, args=(q, i))
        p.start()

    for i in range(0, MAX_PROCESS):
        p.join()

    etime  = time.time()
    print("%s seconds" % (etime - stime))

 

화면 사이즈 구하기

 

MediaQuery.of(context).size
MediaQuery.of(context).size.height
MediaQuery.of(context).size.width

 

 

<샘플예제>

  popupAddItem() {
    showDialog(
      context: context,
      builder: (context) {
        return Dialog(
          shape:
              RoundedRectangleBorder(borderRadius: BorderRadius.circular(10)),
          elevation: 16,
          child: Container(
            height: MediaQuery.of(context).size.height * 0.8,
            width: MediaQuery.of(context).size.width * 1.2,
            child: ListView(
              children: <Widget>[
                SizedBox(height: 20),
                Center(
                  child: Text(
                    "종목등록",
                    style: TextStyle(
                        fontSize: 24,
                        color: Colors.blue,
                        fontWeight: FontWeight.bold),
                  ),
                ),
                TextField(
                  controller: _textController,
                  onSubmitted: _handleSubmitted,
                  decoration: new InputDecoration(
                      hintText: "종목코드 / 종목이름으로 검색하세요.",
                      border: new UnderlineInputBorder(
                          borderSide: new BorderSide(color: Colors.red))),
                ),
                Container(
                  margin: const EdgeInsets.symmetric(horizontal: 4.0),
                  child: IconButton(
                      icon: Icon(Icons.search),
                      onPressed: () => _handleSubmitted(_textController.text)),
                ),
                _buildListItem(),
                SizedBox(height: 20),
                RaisedButton(child: Text("등록"), onPressed: (){},),
              ],
            ),
          ),
        );
      },
    );
  }

 

 

 

 

 

 

 

 

 

 

 

flutter 에서 다국어를 쉽게 적용 할 수 있는 방법을 알아보도록 하겠습니다.

 

우선 아래 사이트에서 한번 살펴 보는 것도 좋을 듯 싶습니다.

 

패키지는 easy_localization 을 사용할 겁니다. 말 그대로 '쉬운 다국어' 이네요.

 

현재 버전은 easy_localization 2.3.2 입니다.

 

 

easy_localization | Flutter Package

Easy and Fast internationalizing and localization your Flutter Apps, this package simplify the internationalizing process .

pub.dev

1. 디펜던시스 에 추가

dependencies:
	easy_localization: ^2.3.2

 

2. 다국어를 쓸수 있게 파일을 작성

   - 프로젝트 폴더 밑에 assets / translations / 폴더를 차례대로 만들고 json 파일 두개를 아래와 같이 만듭니다.

 

3. 그다음엔 다국어 파일의 내용을 작성해야겠죠

   - 길어서 짤렸습니다. 쌍따움표로 묶고 마지막에 , 찍으면 됩니다.

 

4. main.dart 로 돌아와서 내용을 추가해줍니다.

  - 기존 MyApp() 가 child 로 들어갑니다.

void main() {
  runApp(EasyLocalization(
      supportedLocales: [Locale('en', 'US'), Locale('ko', 'KR')],
      path: 'assets/translations',
      fallbackLocale: Locale('en', 'US'),
      child: MyApp()));
}

 

5. MyApp 클래스의 build() 추가해줍니다. 다국어 설정이 바꼈을 때 앱내에서 변환할 수 있도록

  - localizationsDelegates ~~ local: context.locale, 까지입니다.  그 밑은 네비게이터 코드입니다.

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      localizationsDelegates: context.localizationDelegates,
      supportedLocales: context.supportedLocales,
      locale: context.locale,
      routes: <String, WidgetBuilder>{
        // Set routes for using the Navigator.
        '/': (BuildContext context) => Landing(),
        '/home': (BuildContext context) => DashBoard(Colors.white),
        '/login': (BuildContext context) => LoginPage()
      },
    );
  }
}

 

6. 그럼 잊지말고 import 를 추가해줘야 겠죠

import 'package:easy_localization/easy_localization.dart';

 

7. pubspec.yaml 에 assets 을 알려줘야 합니다. 이것도 잊지말고 잘 추가해주세요.

  # To add assets to your application, add an assets section, like this:
  assets:
    - assets/translations/ko-KR.json
    - assets/translations/en-US.json

 

8. 이제 코드에 적용하면 됩니다.

    - 사용 하는 곳에도 import 해주세요

showAlertDialog(context, "failed_login".tr());

 

 

 

 

 

 

 

 

 

 

 


print(getAppVersionBuild())



func getAppVersionBuild() -> String

{

    let version = Bundle.main.infoDictionary!["CFBundleShortVersionString"] as! String

    let build = Bundle.main.infoDictionary!["CFBundleVersion"] as! String



    return version + "." + build

}

 

 

 

 

 

 

Django 에 캐싱을 적용해 보자

 

dataframe 타입의 1000건 이상의 레코드를 DB에서 읽어 웹페이지에 계속 뿌려주는 케이스가 있는데, 

굳이 하루에 한번만 요청에 의해서 DB에 갔다오면 될 듯 하여 캐싱을 적용하기로 했습니다.

 

 

setting.py 에 추가

CACHES = {
    'default': {
        'BACKEND': 'django.core.cache.backends.memcached.MemcachedCache',
        'LOCATION': '127.0.0.1:6379',
    }
}

 

웹서버 실행 시 아래의 오류가 발생한다면

ModuleNotFoundError: No module named 'memcache'

python-memcached 모듈 설치

$ pip install python-memcached

 

views.py 코드에서

key_mydf = 'key_finance'
df = cache.get(key_mydf)

if df is None:
  
  ''' 여기서 부터 DB작업 '''
  
  
  cache.set(key_mydf, df, 86400)
  # 864000 은 하루만큼의 초(sec)

  ''' 중략 '''
  
return render(request, 'finance.html', {'df': df})

- 캐시에 df 가 적재되어 있지 않으면 DB 작업을 통해 데이터를 가져온 후 캐시에 저장한다

- 다음 요청부터 캐시의 데이타를 사용한다

- 하루가 지나면 캐시는 해제된다.

 

 

추가 - 혹시 캐싱이 제대로 안되는 경우 

- 새로 서버를 구축한 후 로그 출력해서 확인 해본 결과 위 내용으로 제대로 캐싱이 먹히지 않았다. 

  캐싱이 안될경우 setting.py 의 middleware 에 아래 두줄 추가하고 

    MIDDLEWARE = [
    'django.middleware.cache.UpdateCacheMiddleware',
    'django.middleware.cache.FetchFromCacheMiddleware',
    ]

같은 setting.py 파일에 아래 4줄을 추가해준다. 

SESSION_ENGINE = 'django.contrib.sessions.backends.cache'
CACHE_MIDDLEWARE_ALIAS = 'default'  # which cache alias to use
CACHE_MIDDLEWARE_SECONDS = 600    # number of seconds to cache a page for (TTL)
CACHE_MIDDLEWARE_KEY_PREFIX = ''

제대로 캐싱이 되는것을 확인.

 

 

 

 

 

 

 

 

 

net-snmp 를 사용 하여 프로그램이 정상 동작 중 , 갑자기 snmp v3로 등록해두었던 장비들이 수집이 되지 않네요.

 

tcpdump 를 통해 확인 한 에러 정보는 장비쪽에서 리포트메세지가 날아오며,

 

UnknownEngineID 라는 메시지입니다.

 

snmp v3 를 테스트 할때 snmpget / snmpwalk 명령어를 사용할 수 있습니다.

 

 

이것을 가지고 샘플을 만들어보면  일반적으로

 

$ snmpget -v3 -l authPriv -u ososoi -a SHA -A brain33a -x AES -X brain33x 10.0.0.207 .1.3.6.1.2.1.1.3.0

iso.3.6.1.2.1.1.3.0 = Timeticks: (185222) 0:30:52.22

 

위와 같이 만들 수 있습니다.

 

여기서 engineid와 engine boots, engine time 을 지정하여 장비쪽으로 전송 할 수 있습니다.

 

unknown engine id 에러가 뜨는 경우 아래와 같이 한번 해보길 바랍니다.

 

$ snmpget -v3 -l authPriv -u ososoi -a SHA -A brain33a -x AES -X brain33x -e 80001f88809fad4d458bbc115f -Z 1/15 10.0.0.207 .1.3.6.1.2.1.1.3.0

iso.3.6.1.2.1.1.3.0 = Timeticks: (205022) 0:34:10.22

 

SNMP Version 3 specific 
  -a PROTOCOL           set authentication protocol (MD5|SHA) 
  -A PASSPHRASE         set authentication protocol pass phrase 
  -e ENGINE-ID          set security engine ID (e.g. 800000020109840301) 
  -E ENGINE-ID          set context engine ID (e.g. 800000020109840301) 
  -l LEVEL              set security level (noAuthNoPriv|authNoPriv|authPriv) 
  -n CONTEXT            set context name (e.g. bridge1) 
  -u USER-NAME          set security name (e.g. bert) 
  -x PROTOCOL           set privacy protocol (DES|AES) 
  -X PASSPHRASE         set privacy protocol pass phrase 
  -Z BOOTS,TIME         set destination engine boots/time 

 

 

그럼 engineid와 boots / time 을 어떻게 구하는냐 ?

아래의 OID를 통해서 알 수가 있습니다.

 

1.3.6.1.6.3.10.2.1.1 snmpEngineID 1 1 An SNMP engine's administratively-unique identifier.
1.3.6.1.6.3.10.2.1.2 snmpEngineBoots 1 1 The number of times that the SNMP engine has
(re-)initialized itself since snmpEngineID
was last configured.
1.3.6.1.6.3.10.2.1.3 snmpEngineTime 1 1 The number of seconds since the value of
the snmpEngineBoots object last changed.
When incrementing this object's value would
caus…

 

Engine ID 입니다.

$ snmpget -v3 -l authPriv -u ososoi -a SHA -A brain33a -x AES -X brain33x 10.0.0.207 1.3.6.1.6.3.10.2.1.1.0

iso.3.6.1.6.3.10.2.1.1.0 = Hex-STRING: 80 00 1F 88 80 9F AD 4D 45 8B BC 11 5F

 

Engine Boots 입니다.

$ snmpget -v3 -l authPriv -u ososoi -a SHA -A brain33a -x AES -X brain33x 10.0.0.207 .1.3.6.1.6.3.10.2.1.2.0

iso.3.6.1.6.3.10.2.1.2.0 = INTEGER: 1

 

Engine Time 입니다.

$ snmpget -v3 -l authPriv -u ososoi -a SHA -A brain33a -x AES -X brain33x 10.0.0.207 .1.3.6.1.6.3.10.2.1.3.0

iso.3.6.1.6.3.10.2.1.3.0 = INTEGER: 2417

 

 

 

centos7에서 테스트 하였습니다.

 

glibc 버전을 확인 하기 위해서 쉘에서 다음의 명령어 들로 확인 할 수 있다.

 

$ getconf -a | grep libc

GNU_LIBC_VERSION                   glibc 2.5

 

$ ldd --version

ldd (GNU libc) 2.5
Copyright (C) 2006 Free Software Foundation, Inc.

 

 

번외로 GLIBC 버전 히스토리

2011-02-01	GLIBC 2.13	glibc 2.13
2010-12-13	GLIBC 2.12.2	
2010-11-30	GLIBC 2.11.3	glibc-2.11.3 released
2010-08-03	GLIBC 2.12.1
2010-05-19	GLIBC 2.11.2	glibc-2.11.2 released
2010-05-03	GLIBC 2.12	glibc 2.12
2009-12-29	GLIBC 2.11.1	glibc-2.11.1 released
2009-11-16	GLIBC 2.10.2	
2009-11-03	GLIBC 2.11	glibc 2.11
2009-05-18	GLIBC 2.10.1
2009-05-09	GLIBC 2.10	2.10 done
2008-11-13	GLIBC 2.9	glibc 2.9
2008-04-12	GLIBC 2.8	glibc 2.8
2007-10-19	GLIBC 2.7	glibc 2.7
2007-07-31	GLIBC 2.6.1	GNU C Library version 2.6.1 release
2007-07-31	GLIBC 2.5.1	GNU C Library version 2.5.1 release
2007-05-17	GLIBC 2.6	glibc 2.6
2006-09-29	GLIBC 2.5	glibc 2.5

 

버전 2.5 ~ 2.13 까지

 

 

 

 

클라우드 서버에 외부 접속 가능하도록 서버를 실행 시키고 

host : 0.0.0.0

port : 8008

 

여러가지 방법으로 시도 해도 클라우드 서버의 Django 서버에 접속 실패 하는 상황

1. 클라우드 홈의 방화벽 설정에서 포트 설정

2. selinux 를 끄고

3. iptable 을 꺼도

 

모두 안됐는데, 다음 한줄이 해결책이었다...

 

firewall-cmd --add-port=8008/tcp

 

만약 서버 재부팅 시에도 계속 유지를 하고 싶다면

 

--permanent 옵션을 추가한다.

 

firewall-cmd --permanent --add-port=8008/tcp

 

클라우드에서 CentOS8 x64 인스턴스를 생성했습니다.

 

저는 vultr.com 이라는 클라우드 호스팅 업체를 이용하고 있습니다. 저렴하고 괜찮은 것 같습니다. 아래링크를 타고 가시면 됩니다.

 

SSD VPS Servers, Cloud Servers and Cloud Hosting by Vultr

Vultr Global Cloud Hosting - Brilliantly Fast SSD VPS Cloud Servers. 100% KVM Virtualization

www.vultr.com

 

CentOS8 이 설치가 됐다고 가정하고

 

 

개발툴 관련 패키지를 차례대로 설치를 합니다.

$ yum -y update

yum -y groupinstall "Development Tools"

yum -y install openssl-devel bzip2-devel libffi-devel

 

 

다음으로 파이썬3.8 을 설치하기 위해 wget 을 설치합니다.

$ yum -y install wget

$ wget www.python.org/ftp/python/3.8.3/Python-3.8.3.tgz

$ tar xvf Python-3.8.3.tgz

 

파이썬3.8 컨피그레이션과 인스톨 수행

$ cd Python-3.8.3

$ ./configure --enable-optimizations

$ make altinstall

 

 

인스톨이 끝나고 나서 제대로 설치 되어 있는지 버전 확인

 

$ python3.8 --version

 

.bash_profile 에 alias 를 등록하여 python 으로 실행하도록 바꿉니다

 

alias python='python3.8'

 

설치된 파이썬 버전 확인

 

혹시, 파이썬 실행시 아래와 같은 오류가 발생한다면

 

ModuleNotFoundError: No module named '_sqlite3

 

$ yum install sqlite-devel

 

설치 후 , 파이썬 재 설치를 진행한다.

 

$ ./configure --enable-optimizations

$ make altinstall

 

 

 

저는 vultr.com 라는 해외 클라우드를 사용하고 있는데 생각보다 저렴합니다. AWS , KT클라우드 보다 쌉니다.

 

작년까진가 인스턴스는 아시아에 싱가포르, 일본 밖에 없었는데 언제부터 생겼는지 서울이 생겼더군요. 

 

일본, 싱가포를 이용할땐 가끔씩 장애도 많이 나고 그랬었는데, 서울로 인스턴스를 옮긴 후부터는 확실히 장애가 거의 없는듯 합니다.

 

젤 저렴한 5천원 짜리도 있고, 적당히 개인사이트 운영에는 만원짜리도 충분 할 듯 싶습니다. 

 

아래 링크 걸어 두었으니 관심 있으신 분들은 저렴하게 나만의 서버를 가지시고 구축해보시면 좋을 것 같습니다.

 

 

 

 

SSD VPS Servers, Cloud Servers and Cloud Hosting by Vultr

Vultr Global Cloud Hosting - Brilliantly Fast SSD VPS Cloud Servers. 100% KVM Virtualization

www.vultr.com

본론으로 저는 여러가지 방법으로 시도 해보았는데 다 실패하고 수동으로 Let's Encrypt 인증서를 생성에 성공 하였습니다.

 

1. certbot 다운

  첫번째로, 적당한 위치에 certbot 을 다운 받습니다.

wget https://dl.eff.org/certbot-auto

 

2. certbot 권한 부여

   실행 권한을 부여합니다.

hmod a+x certbot-auto

 

3. 인증서 생성 명령 실행

  certbot-auto을 이용하여 수동으로 인증서를 생성합니다. vostock.kr 은 제 도메인입니다.

certbot certonly -d vostock.kr --manual --preferred-challenges dns

 

4. DNS TXT 네임과 value 확인

  위 명령을 실행하면 아래 처럼 출력이 되는데 붉은 박스의 첫번째 줄의 challenge 네임과 (_acme~~.kr 까지) 그리고 두번째 줄의 value 를 기억해 둡니다.

  주의!! 여기서 바로 엔터를 눌러서 진행 하면 안됩니다. 5번으로 가서 클라우드 DNS 설정을 추가해야합니다.

 

 

5. 클라우드 DNS 설정 변경

  자신의 클라우드 서버에 접속하여서 DNS 설정에서 (이부분은 클라우드 업체가 다르다 보니 상이할듯 하지만) 

TXT타입으로 DNS 를 추가합니다. 

 

6. SSL 인증서 받기 마무리 하기

   5번을 한후 다시 돌아와서 엔터키를 누르면 인증서를 받았다고 축하해줍니다.

 

주의: 여러번 실패할 경우 강제로 Let's Encryt 에서 일정시간 모든 요청이 거부당합니다. 

        90일 만기이므로 주기적으로 갱신을 시켜줘야 합니다. 이것은 스크립트로 등록해서 자동 처리 할 수 있습니다.

 

crontab -e

위 명령어 실행 후 vi 모드에서 아래 행 추가

0 0 1 * * certbot renew

+ Recent posts