cocos2d-x 005 화면 띄우기 / 씬 전환

cocos2d-x 005 화면 띄우기 / 씬 전환

화면(씬) 전환을 해보겠습니다. 이걸 다 이해한다면, 조금 과장해서 표현하자면 다 배웠다고 말할 수 있습니다.

사실 2D 게임이라는게 그림을 띄우는 겁니다. 글자도 있겠지만요.

그림을 적절하게 띄워주는 것. 이게 다입니다.

어릴 때는 게임 만들기에 특별한 비법 같은게 있을거라고 생각했는데요.

있을지도 모르지만, 기본이 되는 기술은 그림 띄워주기입니다.

여튼 cocos2d-x는 화면(씬) 위에 레이어를 올리고, 레이어 위에 스프라이트(그림)을 올리는 식으로 되어 있습니다.

화면(씬), 레이어, 스프라이트(그림) 순으로 계층을 이루는 것입니다.

나름대로 화면(씬), 레이어, 스프라이트(그림) 구조를 최대한 심플하게 정리해보았습니다.

MainScene.h/cpp 파일의 내용을 아래와 같이 수정합니다.

MainScene.h

——————–

#include “cocos2d.h”
USING_NS_CC;

#pragma once
class MainScene {
public:
    static cocos2d::Scene* createScene();
};

class MainLayer : public cocos2d::Layer {
public:
    CREATE_FUNC(MainLayer);
    virtual bool init();
};

MainScene.cpp

——————–

#include “MainScene.h”

Scene* MainScene::createScene() {
    auto scene = Scene::create();
    auto layer = MainLayer::create();
    scene->addChild(layer);
    return scene;
}

bool MainLayer::init() {
    Size visibleSize = Director::getInstance()->getVisibleSize();
    Vec2 origin = Director::getInstance()->getVisibleOrigin();

    auto sprite = Sprite::create(“main.png”);
    sprite->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y));

    this->addChild(sprite, 0);

    return true;
}

알아보기 쉽도록 클래스를 2개로 만들었습니다. MainScene 이라는 클래스는 화면(씬)을 의미하고, MainLayer 라는 클래스는 레이어를 의미합니다.

이어서 HelloWorldScene.cpp 의 menuCloseCallback 함수를 수정합니다.

기존의 함수 내용은 프로그램을 종료하는 것인데, 전부 주석처리를 하고요. 아래와 같이 수정합니다.

void HelloWorld::menuCloseCallback(Ref* pSender) {
    auto director = Director::getInstance();
    auto Scene = MainScene::createScene();
    Director::getInstance()->pushScene(Scene);

}

이렇게 하면, 메인화면에서 우측 하단의 버튼을 클릭했을 때 menuCloseCallback 함수가 실행되고, 화면(씬)을 생성하고 (auto Scene = MainScene::createScene();), 레이어를 생성하고 (auto layer = MainLayer::create();), 스프라이트(그림)를 생성하게 (auto sprite = Sprite::create(“main.png”);) 됩니다.

 

cocos2d-x 004 그림 띄우기

cocos2d-x 004 그림 띄우기

지난 시간에 이어서 cocos2d-x 를 배웁니다.

사실 화면 띄우기를 해볼 차례인데, 다음으로 미루고 그림 띄우기를 해봅니다.

cocos2d-x는 기본적으로 씬(Scene, 화면)이라는 개념이 있고, 씬 위에 레이어를 올리는 방식으로 보입니다. 레이어 위에는 스프라이트(그림)을 올립니다.

1개의 씬은 1개의 cpp 파일이라고 보면 될 것 같고요. 만약 Temp라는 씬을 만들고 싶다면, TempScene.cpp/h 파일을 만들어주는 것이죠.

씬(화면) – 레이어 – 스프라이트(그림). 이러한 계층 구조입니다.

현재 씬(화면), 레이어, 스프라이트 등 모두 다 준비가 되어 있는데요. HelloWorldScene.cpp 가 그것입니다. 전부 준비가 되어있기 때문에 마지막 부분에서 그림만 바꿔 표시해보겠습니다.

그림을 준비합니다. 그림이나 폰트는 Resources 폴더 안에 준비하면 됩니다.

기본제공되는 HelloWolrd.png 를 복사해서, 같은 크기로 main.png 라는 그림을 준비했습니다.

현재 저희 프로그램은 HelloWorldScene.cpp 의 bool HelloWorld::init() 함수를 부르고 있는데요. 이 함수의 가장 하단 내용은 다음과 같습니다. 헷갈리므로 주석은 제거하고 설명합니다.

auto sprite = Sprite::create(“HelloWorld.png”);
sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 + origin.y));
this->addChild(sprite, 0);

눈치밥으로 HelloWorld.png 라는 그림을 중앙에 띄우는 내용임을 알 수 있습니다.

그럼 바로 그 아래에 코드를 이어 씁니다. main.png 를 띄워봅시다.

auto sprite2 = Sprite::create(“main.png”);
sprite2->setPosition(Vec2(visibleSize.width / 2 + origin.x, visibleSize.height / 2 + origin.y));
this->addChild(sprite2, 0);

이렇게 코드를 덧붙이고 게임을 실행하면 아래 화면처럼 나옵니다.

그림 띄우기 성공입니다.

cocos2d-x 003 새 파일 만들기

cocos2d-x 003 새 파일 만들기

이어서 cocos2d-x 를 배워봅니다. 지지난주만 해도 예제를 진득히 따라쳐야겠다고 생각했으나…

지루해서 못해먹겠습니다.

그냥 제가 중요하다고 생각하는 설명만 쭉쭉 써나가도록 하겠습니다.

지난 시간에 해보았지만, cmd에 아래와 같이 입력하면 파이썬이 알아서 coco2d-x 프로젝트를 만들어줍니다.

cocos new basicgame -p com.cocos2dx.basicgame -l cpp -d c:\cocos2d-x-3.4\projects

정상적으로 수행됐다면 C:\cocos2d-x-3.4\projects\basicgame 이라는 폴더가 생겼을 겁니다.

해당 폴더 내에 proj.win32 폴더로 들어가서 basicgame.sln 파일을 찾아 더블클릭하면 비주얼 스튜디오가 열립니다.

빌드하고 실행하면 아래와 같은 창이 뜹니다.

지난주에 딱 여기까지 배워봤죠.

이렇게 새로 생성된 cocos2d-x 프로젝트는 아래와 같은 폴더 구조를 갖습니다.

보다시피 src 폴더 내에 AppDelegate.cpp/h 와 HelloWorldScene.cpp/h 파일 이렇게 4개 파일이 있습니다. Delegate는 사전을 찾아보니 대표자라는 뜻이라고 하는군요. (저 영어 바보입니다…)

잘은 모르지만 AppDelegate가 먼저 실행되고, 녀석이 HelloWorldScene 을 불러오는 것 같습니다. 실제로 HelloWorldScene.cpp 에는 bool HelloWorld::init() 라는 함수가 있는데요. 이 init 함수 제일 아래 부분에 log(“Hello Game.”); 이라고 입력해두면, 실행 후 콘솔에 Hello Game이 찍히는걸 확인할 수 있습니다.

* cocos2d-x 명령어

log(“contents”);

이제 새 파일을 만들어봅니다. src 폴더 위에서 마우스 우클릭, 클래스 마법사를 클릭합니다.

클래스 마법사 창이 뜨면 클래스 추가 버튼을 클릭합니다.

클래스 이름은 MainScene 이라고 썼습니다. 어떤 이름이든 자유입니다. 그리고나서 .h파일과 .cpp 파일 항목 각각 우측의 쩜쩜쩜(…) 버튼을 클릭해서 경로를 바꿔줘야 합니다.

 

처음에 proj.win32 폴더로 지정되어 있고 파일을 만드시겠습니까? 라고 묻는데 아니요를 선택합니다. 이유는 여기 만들게되면 window 전용 파일이 된다고 합니다. 아시다시피 cocos2d-x는 윈도우, 안드로이드, 아이폰 등 여러 플랫폼에서 구동할 수 있도록 지원하는 게임엔진입니다.

상위 폴더 내의 Classes 폴더를 선택합니다. Classes 폴더 내에서 파일을 만드시겠습니까? 물어보면 예를 선택합니다.

.h파일 뿐 아니라 .cpp 도 똑같이 Classes 폴더 내에 파일을 만들어주십시오. 최종적으로 클래스 마법사 윈도우가 아래와 같이 나타나야 합니다. 우측 하단의 마침 버튼을 클릭합니다.

마치게 되면 아래그림의 좌측처럼 MainScene.cpp/h 파일이 src 폴더 바깥에 생기게 됩니다. 이 파일들을 드래그앤드롭해서 src 폴더 내부로 옮기세요. 실제 파일이 이동되는 것은 아니고 솔루션 탐색기 상으로만 이동합니다.

이렇게 새로 만들어진 h 파일과 cpp 파일은 다음과 같은 내용을 갖습니다.

MainScene.h

——————–

#pragma once
class MainScene
{
public:
 MainScene();
 ~MainScene();
};

MainScene.cpp

——————–
#include “MainScene.h”

MainScene::MainScene()
{
}

MainScene::~MainScene()
{

}

다음 시간에는 이어서 게임 화면을 띄워보겠습니다.

아, 화면이라기엔 거창하고요. 그림을 하나 띄워볼까 합니다.

C++ 파일읽기 파일쓰기

C++ 파일읽기 파일쓰기

올해 목표는 C++로 게임 제작이다. 아무리 엔진을 쓴다지만 너무 기초가 없는 것 같아서 파일입출력을 실습해보았다.

0. 본 코드의 내용은 data/input.txt 파일의 내용을 한 줄씩 읽어들여서, 그 내용을 data/output.txt 에 출력하는 것이다.

1. Windows.h 를 include 했는데 이건 Sleep 함수를 쓰기 위함이다. cmd 에 출력한 스트링을 볼 새도 없이 곧바로 닫히는 문제점이 있어서 Sleep을 걸었다.

2.  문자열 출력에 쓰이는 cout << 문자열 << endl; 명령어가 아무래도 눈에 익지 않아서 println이라는 래퍼함수를 만들었다. 여기서 “식별자를 찾을 수 없습니다.” 에러가 나서 잠깐 헤맸다. 검색해보니 C++는 함수도 변수처럼 상단에 선언되어야 하단에서 사용 가능하다. C만 그렇다고 생각했는데 C++도 그런줄 몰랐다.

3. 파일입출력을 위해 fstream 이라는걸(이걸 라이브러리라고 불러야 하나요? .h가 붙은 것과 붙지 않은 것의 차이는 뭔가요? 전자는 헤더라고 부르는데, 후자도 헤더라고 부르나요?) 인크루드했다. ifstream과 ofstream을 사용하고 닫아줘야 한다고 생각했는데… 닫지 않아도 된다고 한다. 찾아보니 소멸자가 있어서 소멸자에서 닫아준다고. 자바는 파일입출력시에 사용한 객체를 꼭 닫아줘야 하는데, 소멸자가 있다는게 편리할 수도 있다는걸 깨달았다.

4. 파일을 읽고 쓰는 위치는 .cpp 소스파일의 위치를 기준으로 한다.

#include <Windows.h>
#include <iostream>
#include <string>
#include <fstream>

using namespace std;

void println(string s) {
    cout << s << endl;
}

int main() {
    ifstream ins(“data/input.txt”);

    if (!ins.is_open()) {
         println(“파일을 찾을 수 없습니다!”);
         Sleep(1000);
         return 0;
    }

    // 파일이 없으면 생성. 파일이 있으면 덮어씀
    ofstream outs(“data/output.txt”);

    // 이어쓰기하려면 아래와 같이 작성
    // ofstream outs(“data/output.txt”, ios::app);
 
    char buf[1000];

    while (!ins.eof()) {
        // 1000자까지 읽어들이기
        ins.getline(buf, 1000);
        println(buf);

        if (outs.is_open()) {
            outs << buf << endl;
        }
    }

    Sleep(1000);
    return 0;
}

C++로 Hello World 작성

C++로 Hello World 작성

cpp용 게임 엔진인 cocos2d-x 예제를 따라 치고 있었는데, 기본기의 부족을 느껴서 게임엔진 없이 Hello World를 작성해보려고 했다.

시도해보았는데 잘 되지 않았고, 예전에 방통대 다닐 때 구입했던 cpp 책을 참고해서 Hello World를 작성해보았다.

모두에게 너무 쉬운 내용일테지만 개인적으로 참고하기 위해 간단히 정리해본다.

1. Microsoft Visual Studio Express 2013 을 준비한다. 켠다.

2. 파일 – 새 프로젝트 – Win32 콘솔 응용 프로그램을 선택
HelloWorld 라고 쓰고 확인이라고 누른다.

3. Win32 응용 프로그램 마법사 시작 창이 뜬다. 다음 버튼 클릭

4. 응용 프로그램 종류는 콘솔 응용 프로그램 선택, 추가 옵션은 빈 프로젝트 체크.
(미리 컴파일된 헤더와 SDL(Security Develoment Lifecycle 검사는 체크 해제) 마침 버튼 클릭.

5.  솔루션 탐색기에서 소스 파일에 마우스 우클릭 – 추가 – 새 항목. .cpp 선택하고 HelloWorld.cpp 입력 후 엔터

6. HelloWorld 내용을 아래와 같이 작성한다.

1  #include <iostream>
2  #include <windows.h>
3  using namespace std;
4
5  int main() {
6   cout << “Hello World.”;
7   Sleep(1000);
8   return 0;
9  }

원래 2라인과 7라인은 불필요한 라인인데, (windows.h 인크루드하는 라인과 Sleep 명령어 사용하는 라인)
필자의 pc에서 cmd가 켜지자 마자 꺼지는 현상이 있어 부득이 슬립을 걸었다.

7. 빌드 – 솔루션 빌드(F7)로 빌드한다.

8. 디버그 – 디버깅 시작(F5)로 실행한다.

아래와 같은 화면이 나온다.

 

cocos2d-x 002 빌드/화면 띄우기

cocos2d-x 002 빌드/화면 띄우기

계속해서 cocos2d-x 학습을 이어갑니다.
올해의 목표는 자작게임을 만들어서 안드로이드/아이폰 마켓에 출시하는 겁니다.

참고로 이 글은 http://www.hanbit.co.kr/media/openbook/cocos2d-x/04.html 의 내용을 보고 썼습니다.
자세한 내용은 여기 다 나와있습니다.

cmd 들어가서 cocos 를 칩니다.
오류가 나지 않고 뭐시기뭐시기 나오면 환경변수 잘 잡힌겁니다. 에러나면 001 세팅 편으로 돌아가세요.

참고삼아 cmd 명령어를 적어둡니다.

* 도움말 보기
cocos [명령어] –help
예) cocos new –help

* 새 프로젝트 만들기
cocos new [프로젝트명] -p [패키지명] -l [사용할 언어] -d [생성할 경로]
예) cocos new minigame -p com.cocos2dx.minigame -l cpp -d c:\cocos2d-x-3.4\projects

이제 새 프로젝트를 만들어봅시다.
cmd에 아래와 같이 써넣습니다.

cocos new minigame -p com.cocos2dx.minigame -l cpp -d c:\cocos2d-x-3.4\projects

끝날 때까지 기다립니다.
끝나고 나니 C:\cocos2d-x-3.4\projects\minigame 이라는 폴더가 생겼습니다.

C:\cocos2d-x-3.4\projects\minigame\proj.win32 폴더에 들어가서 minigame.sln 을 더블클릭, Visual Studio 로 열어봅니다.

보기 – 솔루션 탐색기 로 프로젝트 구성 트리를 볼 수 있습니다.
솔루션 탐색기 에서 minigame – 우클릭 – 다시 빌드 를 합니다.

여기서 잠깐! 저는 이 부분에서 빌드 오류가 났고 엄청 고생을 했습니다.
사실 처음에 Visual Studio 2017 을 설치했었는데 그걸론 어떤 짓을 해도 빌드가 되지 않습니다.
구글링 결과 cocos2d-x-3.4는 Visual Studio 2013 에 최적화 되어 있습니다.
삽질 마시고 Visual Studio Express 2013 for windows desktop 을 구하셔서 설치하시기 바랍니다.

(깔았던 2017 언인스톨 하는거 없이 2013을 새로 깔았습니다. 빌드 잘 되네요.)

F5키를 눌러서 실행해봅니다.

아래와 같이 나왔습니다. 나이스 샷!

세팅이 끝났고 화면 띄우기까지 된겁니다.

이제 모든 것은 시간문제라는 신념을 가지고 열심히 개발해보겠습니다.

cocos2d-x 001 세팅

cocos2d-x 001 세팅

안녕하세요, 흑곰입니다. 오늘부터 cocos2d-x 공부를 시작합니다.

목표는 올해(2018년) 안으로 안드로이드/아이폰 마켓에 게임 출시입니다.

목적은 VC++ 학습 및 재택근무 개발자가 되기 위한 발판 마련입니다. 참고로 VC++에 대해 1도 모릅니다. 할줄 아는건 비주얼베이직이랑 자바입니다.

본 내용은 http://www.hanbit.co.kr/media/openbook/cocos2d-x/03.html 를 참고해서 적습니다. 자세한 내용이 필요하시면 여길 보세요.

우선 세팅입니다. 모든 개발의 시작은 세팅입니다.

개인적으로 세팅이야말로 개발에서 제일 어려운 부분이라고 생각합니다.

1. 컴퓨터 준비

cocos2d-x와 VC++을 배우기 위해 새 노트북을 구입했습니다. 놀랍죠? 저도 놀랍습니다.

레노버 / CPU i5 / RAM 16GB / SSD 250GB / 87만 8천원.

2. VC++설치

마이크로소프트에서 제공하는 통합 툴이 있습니다. 비주얼 스튜디오 라고 합니다. 깔아봅시다.

다운로드 주소는 http://www.visualstudio.com/downloads입니다.

저는 Visual Studio Community 2017 (학생, 오픈 소스 및 개인 개발자를 위한 모든 기능을 갖춘 무료 IDE) 에서 무료다운로드 버튼을 눌러 다운 받았습니다.

설치할 때는 보이는 체크 박스 다 체크해서 풀설치 했습니다.

* 내용 추가 : Visual Studio 2017가 아닙니다! 빌드가 안되어 이리저리 확인결과 cocos2d-x 3.4 버전은 Visual Studio 2013에 최적화되어 있습니다. Visual Studio Express 2013 for windows desktop 버전을 구하셔야 합니다. 저도 2017을 깔았다가, 도저히 빌드가 안되어서 다시 2013을 깔았습니다. (2017 언인스톨은 하지 않았습니다)

3. 파이썬 2.7 설치

cocos2d-x 를 퍼블리싱하기 위해서는 파이썬 2.7이 필요하다고 합니다. 3점대는 안된다고 하네요. 심지어 2.7과 3을 동시에 설치하는 것도 안된다고 합니다. 사실 이것때문에 컴퓨터를 새로 산 측면이 큽니다. 기존에 파이썬 3점대를 쓰고 있고, 컴을 가상화로 돌리기엔 너무 느리고.

파이썬을 다운받을 수 있는 주소는 여깁니다. https://www.python.org/downloads/

Download Python 2.7.14을 클릭하세요.

혹은 https://www.python.org/ftp/python/2.7.14/python-2.7.14.msi 이 주소를 클릭해 바로 다운받습니다.

받았으면 그대로 설치합니다. 경로는 기본경로인 C:\Python27 그대로 둡니다.

3-1. 파이썬 환경변수 잡기

환경변수는 파일 탐색기에서 내PC 우클릭 – 속성 클릭 – 고급 시스템 설정 클릭 – 고급 탭 – 하단의 환경 변수 버튼 클릭 – 하단의 시스템 변수 중 Path를 수정해서 끝부분에 C:\Python27 을 기입해넣습니다.

무슨 말인지 모르시겠다고요? 절대 포기하지 마세요. 환경변수 추가하는 법 검색해서 꼭 하세요.

환경변수를 잡아놓으면 cmd 에서 python -V 를 입력했을 때 Python 2.7.14 요렇게 글자가 나옵니다.

4. cocos2d-x 다운

http://www.cocos2d-x.org/download 에 들어갑니다. 3.4 버전을 찾으면 된다네요. 근데 웬걸? 3.1버전이 최신입니다. 책에서는 3.4버전이 있다고 하던데…

찾으시겠어요? 아니요. 저는 못찾겠습니다.

주소를 바로 때려넣어 다운로드 받아봅시다.

http://www.cocos2d-x.org/filedown/cocos2d-x-3.4.zip

이렇게 cocos2d-x 3.4버전을 얻었습니다.

C:\cocos2d-x-3.4 에 압축을 풀어 넣습니다. 참고로 cocos2d-x-3.4 폴더 안에 또 cocos2d-x-3.4 폴더가 있지 않도록 잘 풀어넣어야 합니다.

cocos 환경변수를 잡아봅시다. 파이썬 파일을 실행하면 자동으로 잡아준다고 하네요.

아! 왜 파이썬을 깔았는지 이제 이해가 되었습니다.

cmd

cd c:\cocos2d-x-3.4

setup.py

파이썬으로 실행합니다. NDK_ROOT, ANDROID_SDK_ROOT, ANT_ROOT 이 3가지에 대해 뭘 입력해달라고 하는데, 엔터 3번 쳐서 무시하고 넘어갑니다.

파이썬 현재시간 얻기 함수 getCurrentTime()

파이썬 현재시간 얻기 함수 getCurrentTime() 

아래와 같이 함수를 만들어보았다.

time 패키지를 임포트 해야 하며, 시간을 ‘20180102004055’ 형태로 얻는다.

더 좋은 방법이 있겠지만 검색해보니 또 다른 패키지를 이것저것(ex: gmtime, strftime) 더 추가해야 하는 것 같아서 그냥 만들었다.

import time

# 시간을 ‘20180102004055’ 형태로 얻는다.
def getCurrentTime():
    timeArr = time.ctime().split(” “)

    # time.ctime() 결과는 다음과 같다.
    # Tue Jan 2 00:07:54 2018

    result = “”
    result += convNumToStr(timeArr[5], 4)
    result += convNumToStr(convMonthToNum(timeArr[1]), 2)
    result += convNumToStr(timeArr[3], 2)
    result += convNumToStr(str(timeArr[4]).replace(“:”,“”), 6)

    return result

# 알파벳으로 된 Month를 2자리 숫자 문자열로 변환한다.
def convMonthToNum(_month):
    if (_month is None or _month == “”):
        return “00”

    if (_month == “Jan”):
        return “01”

    elif (_month == “Feb”):
        return “02”

    elif (_month == “Mar”):
        return “03”

    elif (_month == “Apr”):
        return “04”

    elif (_month == “May”):
        return “05”

    elif (_month == “Jun”):
        return “06”

    elif (_month == “Jul”):
        return “07”

    elif (_month == “Aug”):
        return “08”

    elif (_month == “Sep”):
        return “09”

    elif (_month == “Oct”):
        return “10”

    elif (_month == “Nov”):
        return “11”

    elif (_month == “Dec”):
        return “12”

    return “00”


# 숫자를 특정 자리수의 문자열로 변환한다.
def convNumToStr(_str, _len):
    if (_str is None):
        _str = “”
    else:
        _str = str(_str)

    if (_len < 0):
        _len = 0

    realLen = len(_str)

    if (realLen == _len):
        return _str

    if (realLen < _len):
        gap = _len realLen
        for i in range(0, gap):
            _str = “0” + _str

        return _str

    return _str[realLen _len, realLen]

파이썬 패키지 설치 (Cannot fetch index base URL http://pypi.python.org/simple/)

파이썬 패키지 설치 (Cannot fetch index base URL http://pypi.python.org/simple/)

오늘 파이썬 패키지에 대해 알아낸 바가 있어서 간략히 쓴다. 특별한 건 아니고, 누구나 알만한데 나만 몰랐던 기초적인 내용이다.

우선, 우분투 기준 파이썬 설치는 apt-get install python3 라고 쓰면 된다.

이후 파이썬 패키지 설치는 pip 명령어를 쓰면 된다. pip 은 파이썬 패키지 관리자로, 약어는 Pip Installs Packages 라는 재귀적인 약어이다. 예를 들면 flask 패키지를 설치하고 싶을 경우, pip install flask 라고 쓰면 된다.

그런데 내가 이용하는 카페24 서버에서는 pip 명령어가 동작하지 않았다. Cannot fetch index base URL http://pypi.python.org/simple/ 라는 로그가 발생하였다. 구글링 결과 pip 버전이 너무 높다거나, 프록시 서버 문제라는 의견이 있었는데, 영어 해석도 어렵고 이것저것 해봐도 잘 안돼서 pip 사용은 포기했다.

대안으로 pip을 쓰지 않고 패키지를 설치하는 방법이 있었다.

(1) 패키지를 다운받는다. (github에서 해당 패키지를 찾고, Download ZIP 으로 다운한다)

(2) 압축을 풀면 디렉토리가 나온다. FTP로 서버에 올린다.

(3) 해당 디렉토리 안에 setup.py 파일이 있다. 서버에서 python setup.py install 이라고 쓰면 설치가 진행된다.

이렇게 진행해보니 관련된 패키지가 몽땅 설치되는 것은 아닌지, 내가 만든 프로그램을 실행시키기에는 부족했다.

목적은 flask 를 설치하는 것이었는데, click, itsdangerous 라는 패키지를 차례로 설치하게 되었다. 모두 FTP 업로드 후 python setup.py install 을 이용하여 순조롭게 설치할 수 있었다.

최종적인 문제가 있었다면 내가 만든 패키지를 파이썬이 인식하지 못했다는 점이다. 로컬에서는 문제가 없었는데 말이다. 해결책은 패키지명 폴더 안에 __init__.py 를 넣는 방법이었다. 로컬에서는 윈도우 환경이어서 그런지 몰라도 폴더 안에 __init__.py 가 존재하지 않아도 해당 패키지를 잘 찾았다. 그런데 서버 환경에서는 리눅스(우분투)여서 그런지 파이썬 버전 문제인지는 몰라도 __init__.py 파일이 없으면 패키지를 인식하지 못하는 문제가 있었다.

여튼 패키지는 잘 설치되었고, flask 서버도 잘 떠서 다행이다.

[Toad] 방금 운영에서 실행된 SQL쿼리 얻기

[Toad] 방금 운영에서 실행된 SQL쿼리 얻기

토드(Toad)에서 관리자 권한 있을 때만 사용 가능하다.

Toad – 상단 메뉴의 Database – Monitor – SGA Trace/Optimization 을 클릭하면 SGA Trace 탭이 나온다.

1. 좌측 상단의 콤보박스와 인풋박스를 동원해 필터를 건다. 예를 들어 콤보박스를 Select Statements로 하면 Select 문만 나온다.

2. 우측의 깔대기 모양 버튼(Flush 버튼)을 누르면 쿼리 목록이 비워진다.

3. 운영에서 특정 기능을 수행한 후, 화살표 2개가 원형으로 배치된 버튼(새로고침 버튼)을 누르면 SQL쿼리가 목록에 추가되어 나타난다.

[자바 JNA] 익스플로러 창개수 관리 데몬

[자바 JNA] 익스플로러 창 개수 관리 데몬

익스플로러를 띄우고, 창 개수를 관리해주는 데몬이다. 인터넷 옵션에서 원하는 URL을 홈페이지로 설정해두고 실행시키면 된다.

– 임포트 필요한 jar : (1) jna-4.5.0.jar / (2) jna-platform-4.5.0.jar

– 사용법

1. 아래 내용으로 프로젝트를 만든다.

2. 이클립스에서 프로젝트명 우클릭 – Build Path – Configure Build Path – 좌측메뉴의 Java Build Path – Libraries 탭 – Add JARs 버튼 – 클릭하고 라이브러리 (1) jna-4.5.0.jar / (2) jna-platform-4.5.0.jar 이렇게 2개 선택하고 – OK 버튼 클릭.

3. 이클립스에서 File – Export – Runnable JAR File 로 jar 파일 1개로 묶는다. (IEChecker.jar)

4. 동일한 위치에, IEChecker.bat 파일을 메모장으로 만든다. 내용은 아래와 같이 한다.

javaw -jar ./IEChecker.jar handleCount=2 sleep=200 closeHwpPwd=0

handleCount 는 목표하는 창 개수이다. 2로 지정되어 있다면 익스플로러 창을 2개로 유지한다.

sleep 은 루프돌 때의 대기시간이다. 밀리세컨드로 적으면 되며 100 에서 200 사이 값을 주면 적당하다.

closeHwpPwd 은 혹시나 해서 만든 옵션이다. 0 으로 놓고 돌리면 된다. 1 일 경우 한글(프로그램) 암호입력 창이 나올 경우 닫는다.

5. cmd 창을 열고 해당 bat 파일을 실행한다. 끝.

package com;

import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.util.ArrayList;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinUser.WNDENUMPROC;

public class MainClass {

    // 한글 암호입력창일 경우 닫기 옵션일 경우
    public static boolean optionCloseHwpPwd = false;
   
    // 로봇 객체
    public static Robot robot = null;

   
    /**
     * 메인 함수. IE Checker 를 실행한다.
     *
     * @param args
     */
    public static void main(String[] args) {

        double mx = 1000;
        double my = 1000;

        int targetHandleCount = 1;
        int sleepMilSecond = 200;
       
        try {
            System.out.println(“IE Checker Run.”);
           
            targetHandleCount = getArgumentInt(args, “handleCount”);
            if (targetHandleCount < 1) {
                targetHandleCount = 1;
            }
           
            sleepMilSecond = getArgumentInt(args, “sleep”);
            if (sleepMilSecond < 1) {
                sleepMilSecond = 200;
            }
           
            int closeHwpPwd = getArgumentInt(args, “closeHwpPwd”);
            if (closeHwpPwd == 1) {
                optionCloseHwpPwd = true;
            }

           
            System.out.println(“handleCount : ” + targetHandleCount);
            System.out.println(“sleep : ” + sleepMilSecond);
            System.out.println(“closeHwpPwd : ” + optionCloseHwpPwd);
           
            robot = new Robot();

            System.out.println(“Loop Begin.”);
           
            // 마우스가 화면 좌상단 10, 10 이내이면 프로그램 종료.
            while (mx > 10 || my > 10) {
                Point point = MouseInfo.getPointerInfo().getLocation();
                mx = point.getLocation().getX();
                my = point.getLocation().getY();

                // printAllHandles();
               
                if (targetHandleCount > checkHandleCount(“IEFrame”)) {
                    executeIEProcess();
                }
               
                Thread.sleep(sleepMilSecond);
            }
           
            System.out.println(“Loop End.”);

        } catch (Exception e) {
            e.printStackTrace();
        }
       
        System.out.println(“IE Checker End.”);
    }
   
   
    /**
     * 아규먼트 배열에서 특정한 키값으로 이퀄(=) 우측의 숫자 값을 얻는다.
     *
     * @param args
     * @param nameToFind
     * @return
     */
    public static int getArgumentInt(String[] args, String nameToFind) {
        String str = getArgumentString(args, nameToFind);
        if (str == null || str.length() == 0) {
            return 0;
        }
       
        int result = 0;
        try {
            result = Integer.parseInt(str);
       
        } catch (Exception e) {
            e.printStackTrace();
        }
       
        return result;
    }

   
    /**
     * 아규먼트 배열에서 특정한 키값으로 이퀄(=) 우측의 문자열 값을 얻는다.
     *
     * @param args
     * @param nameToFind
     * @return
     */
    public static String getArgumentString(String[] args, String nameToFind) {
        if (args == null || args.length == 0) {
            return “”;
        }
       
        if (nameToFind == null || nameToFind.length() == 0) {
            return “”;
        }
       
        String oneArg = “”;
        int cnt = args.length;
        for (int i=0; i<cnt; i++) {
            oneArg = args[i];
            if (oneArg == null || oneArg.length() == 0) {
                continue;
            }
           
            if (oneArg.toLowerCase().startsWith(nameToFind.toLowerCase() + “=”)) {
                int eqIdx = oneArg.indexOf(“=”);
                String result = oneArg.substring(eqIdx + 1);
                return result;
            }
        }
       
        return “”;
    }

   
    /**
     * 익스플로러를 실행한다.
     *
     * @throws Exception
     */
    public static void executeIEProcess() throws Exception {
       
        ArrayList list = new ArrayList();
        list.add(“C:\\Program Files\\Internet Explorer\\iexplore.exe”);
        // list.add(“http://en.wikipedia.org/“);

        ProcessBuilder pb = new ProcessBuilder(list);
        pb.start();
    }
   
   
    /**
     * 현재 실행중인 모든 핸들의 정보를 출력한다. (로그출력용)
     */
    /*
    public static void printAllHandles() {

        try {
            User32.INSTANCE.EnumWindows(new WNDENUMPROC() {

                public boolean callback(HWND hWnd, Pointer arg1) {
                    char[] windowText = new char[512];
                    User32.INSTANCE.GetWindowText(hWnd, windowText, 512);
                    String wText = Native.toString(windowText);
                    RECT rectangle = new RECT();
                    User32.INSTANCE.GetWindowRect(hWnd, rectangle);

                    if (wText.isEmpty() ||
                        !(User32.INSTANCE.IsWindowVisible(hWnd) &&
                            rectangle.left > -32000)) {
                        return true;
                    }

                    int count = 0;
                   
                    char[] c = new char[512];
                    User32.INSTANCE.GetClassName(hWnd, c, 512);
                    String clsName = String.valueOf(c).trim();

                    StringBuffer buff = new StringBuffer();

                    buff.append(“번호:” + (++count));
                    buff.append(“,텍스트:” + wText);
                    buff.append(“,” + “위치:(“);
                    buff.append(rectangle.left + “,” + rectangle.top + “)~(“);
                    buff.append(rectangle.right + “,” + rectangle.bottom);
                    buff.append(“),” + “클래스네임:” + clsName);

                    System.out.println(buff.toString());

                    return true;
                }
            }, null);
           
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    */
   
   
    /**
     * 현재 실행 중인 핸들의 개수를 얻는다.
     *
     * @param targetHandleName
     * @return
     */
    public static int checkHandleCount(String targetHandleName) {
       
        if (targetHandleName == null || targetHandleName.length() == 0) {
            return -1;
        }
       
        final String finHandleName = targetHandleName;
       
        final int[] handleCount = new int[1];
        handleCount[0] = 0;
       
        try {
            User32.INSTANCE.EnumWindows(new WNDENUMPROC() {
                public boolean callback(HWND hWnd, Pointer arg1) {
                    char[] windowText = new char[512];
                    User32.INSTANCE.GetWindowText(hWnd, windowText, 512);
                    String wText = Native.toString(windowText);
                    RECT rectangle = new RECT();
                    User32.INSTANCE.GetWindowRect(hWnd, rectangle);

                    if (wText.isEmpty() ||
                        !(User32.INSTANCE.IsWindowVisible(hWnd) &&
                            rectangle.left > -32000)) {
                        return true;
                    }

                    int count = 0;
                   
                    char[] c = new char[512];
                    User32.INSTANCE.GetClassName(hWnd, c, 512);
                    String clsName = String.valueOf(c).trim();

//                    StringBuffer buff = new StringBuffer();
//
//                    buff.append(“번호:” + (++count));
//                    buff.append(“,텍스트:” + wText);
//                    buff.append(“,” + “위치:(“);
//                    buff.append(rectangle.left + “,” + rectangle.top + “)~(“);
//                    buff.append(rectangle.right + “,” + rectangle.bottom);
//                    buff.append(“),” + “클래스네임:” + clsName);
//
//                    System.out.println(buff.toString());
                   
                    if (clsName != null) {
                        if (clsName.equals(finHandleName)) {
                            handleCount[0]++;
                        }
                       
                        // 한글 암호입력창일 경우 닫기 옵션일 경우
                        if (optionCloseHwpPwd) {
                            // 한글 암호입력창일 경우 닫기 시도한다.
                            if (clsName.equals(“HNC_DIALOG”)) {
                                // 셋포커스(잘 동작하지 않지만 그래도.)
                                User32.INSTANCE.SetForegroundWindow(hWnd);
                               
                                // 현재 포커스된 창 얻기
                                HWND curHwnd = User32.INSTANCE.GetForegroundWindow();
                                if (curHwnd != null) {
                                    String curWinName = getClassNameFromHandle(curHwnd);
                                   
                                    // 현재 포커스된 창이 한글 암호입력 창이라면 종료
                                    if (curWinName != null && curWinName.equals(“HNC_DIALOG”)) {
                                        // 종료
                                        AltF4(200);
                                        return true;
                                    }
                                }
                            }
                        }
                    }

                    return true;
                }
            }, null);
           
        } catch (Exception e) {
            e.printStackTrace();
        }
       
        return handleCount[0];
    }
   
   
    /**
     * 핸들의 이름을 얻는다.
     *
     * @param hWnd
     * @return
     * @throws Exception
     */
    public static String getClassNameFromHandle(HWND hWnd) {
        if (hWnd == null) {
            return “”;
        }
       
        String clsName = “”;
       
        try {
            // 핸들의 클래스 네임 얻기
            char[] c = new char[512];
            User32.INSTANCE.GetClassName(hWnd, c, 512);
            clsName = String.valueOf(c).trim();
           
        } catch (Exception e) {
            e.printStackTrace();
        }
       
        return clsName;
    }
   
   
    public static HWND castHwnd(Object obj) {
        try {
            return (HWND) obj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }
   
   
    public static void AltF4(int delay) {
        try {
            if (robot == null) {
                robot = new Robot();
            }
           
            robot.keyPress(KeyEvent.VK_ALT);
            robot.keyPress(KeyEvent.VK_F4);
            robot.keyRelease(KeyEvent.VK_F4);
            robot.keyRelease(KeyEvent.VK_ALT);
            robot.delay(delay);
       
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}
 

[자바 JNA] 실행중인 윈도우 찾는대로 종료하기

[자바 JNA] 실행중인 윈도우 찾는대로 종료하기

// 필요한 파일 : (1)jna-4.5.0.jar, (2)jna-platform-4.5.0.jar

package com.thkmon.hwndtest;

import java.awt.MouseInfo;
import java.awt.Point;
import java.awt.Robot;
import java.awt.event.KeyEvent;
import java.util.HashMap;

import com.sun.jna.Native;
import com.sun.jna.Pointer;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinDef.RECT;
import com.sun.jna.platform.win32.WinUser.WNDENUMPROC;

public class MainClass {

    public static Robot robot = null;

    public static void main(String[] args) {

        double mx = 1000;
        double my = 1000;

        try {
            robot = new Robot();

            // 마우스가 화면 좌상단 100, 100 이내이면 프로그램 종료.
            while (mx > 100 || my > 100) {

                Point point = MouseInfo.getPointerInfo().getLocation();
                mx = point.getLocation().getX();
                my = point.getLocation().getY();

                Thread.sleep(100);

                HashMap hwndMap = checkHwnd();

                if (hwndMap != null) {
                    HWND hWnd = castHwnd(hwndMap.get(“HNC_DIALOG”));
                    if (hWnd != null) {
                        System.err.println(“찾음.”);
                        Thread.sleep(100);

                        // 셋포커스(동작하지 않네…)
                        User32.INSTANCE.SetForegroundWindow(hWnd);
                        HWND curHwnd = User32.INSTANCE.GetForegroundWindow();
                        if (curHwnd != null) {
                            String curWinName = getClassNameFromHandle(curHwnd);
                            System.out.println(“curWinName : “ + curWinName);

                            if (curWinName != null && curWinName.equals(“HNC_DIALOG”)) {
                                // 종료
                                AltF4(200);
                            }
                        }
                    }

                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        }

        System.err.println(“사용자 명령으로 종료.”);
        System.exit(0);
    }

    public static HWND castHwnd(Object obj) {

        try {
            return (HWND) obj;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public static void AltF4(int delay) throws Exception {
        robot.keyPress(KeyEvent.VK_ALT);
        robot.keyPress(KeyEvent.VK_F4);
        robot.keyRelease(KeyEvent.VK_F4);
        robot.keyRelease(KeyEvent.VK_ALT);
        robot.delay(delay);
    }

    public static HashMap checkHwnd() {

        final HashMap hwndMap = new HashMap();

        User32.INSTANCE.EnumWindows(new WNDENUMPROC() {
            int count = 0;

            public boolean callback(HWND hWnd, Pointer arg1) {
                char[] windowText = new char[512];
                User32.INSTANCE.GetWindowText(hWnd, windowText, 512);
                String wText = Native.toString(windowText);
                RECT rectangle = new RECT();
                User32.INSTANCE.GetWindowRect(hWnd, rectangle);
                // get rid of this if block if you want all windows
                // regardless
                // of whether
                // or not they have text
                // second condition is for visible and non minimised windows
                if (wText.isEmpty() || !(User32.INSTANCE.IsWindowVisible(hWnd) && rectangle.left > -32000)) {
                    return true;
                }

                String clsName = getClassNameFromHandle(hWnd);
                if (clsName != null && clsName.length() > 0) {

                    // StringBuffer buff = new StringBuffer();
                    //
                    // buff.append(“번호:” + (++count));
                    // buff.append(“,텍스트:” + wText);
                    // buff.append(“,” + “위치:(“);
                    // buff.append(rectangle.left + “,” + rectangle.top + “)~(“);
                    // buff.append(rectangle.right + “,” + rectangle.bottom);
                    // buff.append(“),” + “클래스네임:” + clsName);
                    //
                    // System.out.println(buff.toString());

                    hwndMap.put(clsName, hWnd);
                }

                return true;
            }
        }, null);

        return hwndMap;
    }

    public static String getClassNameFromHandle(HWND hWnd) {
        if (hWnd == null) {
            return “”;
        }

        // 핸들의 클래스 네임 얻기
        char[] c = new char[512];
        User32.INSTANCE.GetClassName(hWnd, c, 512);
        String clsName = String.valueOf(c).trim();
        return clsName;
    }
}

[파이썬] 파이썬 기초팁 11가지

[파이썬] 파이썬 기초팁 11가지

필자는 원래 자바/JSP 개발자이고, 주말 동안 짬을 내어 파이썬을 실습해보았다.

실습하며 배운 몇 가지 팁을 기록해본다.

기본서를 보면 다 나오는 것들이다. (근데 왜 난 몰랐을까…)

1. 임포트 방법

상단에

from 패키지명 import 파일명

라고 쓰면 된다.

예를 들어 폴더트리 구조상 프로젝트명/pack/dir1/TempFile.py 이라고 되어 있다면,

from pack.dir1 import TempFile

이렇게 임포트하면 된다.

2. 임포트한 파일(.py) 내의 함수 사용

그렇다면 TempFile 내의 함수는 어떻게 사용할까?

만약 TempFile.py 안에 함수를 정의해두었다면, (ex: def func_name(): ~ )

TempFile.func_name() 식으로 사용할 수 있다.

3. 글로벌 함수 사용

아래와 같이 함수 안에서 함수 바깥의 변수를 쓰고자 한다면,

함수 안쪽에서 사용하기 전, “global 변수명” 이라 쓰면 된다.

상당히 특이하다고 생각했다.

g_var = None

def 함수명()

    global g_var

    g_var = “변수값”

    print(“g_var : ” + g_var)

4. 파이썬의 Null 사용

파이썬은 Null 이 아니라 None 을 쓴다.

temp_var = None

위와 같이 쓰면 된다.

널 체크는 다음과 같이 한다.

if (temp_var is None):

    print(“temp_var is None”)

널이 아닌 경우를 체크하려면 다음과 같이 한다.

if (temp_var is not None):

    print(“temp_var is not None”)

5. 클래스의 선언

아래와 같은 방식이다.

class 클래스명():

    def __init__(self):

        print(“생성자 실행”)

    def 함수명(self)

        print(“함수내용”)

클래스를 갖다쓸 때는(생성할 때는), 아래와 같이 쓰면 된다.

# 생성

변수명 = 클래스명()

# 함수사용

변수명.함수명()

6. 함수 아규먼트(함수 인자)의 기본값 설정

파이썬은 함수 아규먼트의 기본값을 제공할 수 있다.

예를 들자면,

def 함수명(arg1, arg2=None)

이런 식이다. 이 경우 함수를 사용할 때, 인자를 1개 넘겨도 되고 2개 넘겨도 된다.

메서드 오버로딩 비슷한 느낌이다.

참고로 인자 개수가 다른 경우는 위와 같이 오버로딩할 수 있는데,

인자 데이터형이 다른 경우는 조금 더 복잡했던 것 같다.

(함수 정의하는 라인에 타입을 쓸 수 없으니, 함수 안에서 타입을 걸러줘야 하는 것 같았다.

자세한 것은 구글링하시길. 저도 몰라유~)

7. int의 출력 (int to string)

# 에러나는 경우

a = 111

print(“숫자 출력 : ” + a)

위와 같이 int를 string이랑 결합해서 출력하면 에러가 나더라.

이건 자바도 부드럽게(?) 지원되는 부분인데… 파이썬한테 약간 실망했다.

아래와 같이 int를 string으로 형 변환 해줘야 한다.

# 정상출력

a = 111

print(“숫자 출력 : ” + str(a))

8. 리스트와 튜플의 사이 형변환 (list to tuple, tutple to list)

리스트와 튜플은 일종의 데이터형이다.

리스트는 변경가능(append 가능)하고, 튜플은 변경불가능하다.

현재까진 이 정도 차이점 밖에 모르겠고,

하여간 튜플은 변경불가능해서 참 불편하더라.

형변환을 해서 편하게 쓸 수 있었다.

# tuple to list

tuple_test = (1, 2, 3, 4)

list_test = list(tuple_test)

# list to tuple

list_test = [1, 2, 3, 4]

tuple_test = tuple(list_test)

9. 파이썬의 문자열 잘라내기(파이썬의 substring)

파이썬은 따로 substring이 함수로 제공되지 않는다.

예를 들어,

a = “012345” 의 좌측 3자를 잘라쓰려면

a[0:3] 이렇게 자르면 된다.

상당히 특이하다. char의 배열을 다루는 기분이다.

10. 파이썬의 for문

우리가 아는 for문은 크게 2가지로 나뉜다.

익히 알고 있는 기본for문과 배열/리스트형 for문이다.

파이썬은 후자인 배열/리스트형 for문 만을 지원한다.

무슨 소리냐고?

예를 들어 자바에서 기본 for문은 다음과 같이 쓴다.

for (int i=0; i<10; i++) {

    // 내용

}

자바에서 배열/리스트형 for문은 다음과 같이 쓴다.

int arr[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};

for (int num : arr) {

    // 내용

}

VB/VBA의 기본 for문은 다음과 같이 쓴다.

for i = 1 to 10

    // 내용

next i

파이썬은 아래와 같이 배열/리스트형 for문만 지원한다.

for i in 리스트형변수:

    print(i)

그래서 만약 기본 for문 처럼 쓰고 싶다면, range 함수를 응용해야 한다.

range는 정수형 리스트를 만들어주는 함수다.

아래와 같이 쓰면 된다.

for i in range(1, 10):

    // 내용

11. 파이썬의 예외처리(파이썬의 try ~ except)

자바는 try ~ catch ~ finally 가 지원되는데, 파이썬은 try ~ except ~ finally 가 지원된다.

예를 들면 아래와 같다.

try:

    print(“수행내용”)

except Exception as e:

    print(“에러내용 : ” + e)

finally:

    print(“함수의 끝”)

여기서 finally는 생략해도 된다.

그리고 거의 모든 인터넷상의 예제가 except: 로 쓰곤 하는데, 나는 except Exception as e: 가 좋은 것 같다.

자바 개발자라 그런가.

오늘 실습은 이 정도였던 것 같다.

파이썬에 익숙하지도 않고, 개인적으로 나는 역시 올드(?)한 개발자라는 생각을 했다.

특히 substring이 익숙치 않아서 관련 함수를 몇 개 만들어버렸다.

앞으로도 좀 덜 익숙한 것들은 함수로 만들 것 같다.

프로그래밍계의 유명한 격언으로 “바퀴를 재발명하지 마라”는 문장이 있는데, 난 아직 한참 멀었다는 생각을 한다.

끝.

[Javascript] 숫자인지 체크 (checkNumbersOnly)

[Javascript] 숫자인지 체크 (checkNumbersOnly)

문자열이 숫자로만 구성되어 있는지 체크하는 자바스크립트 함수.


for문을 사용한 기존 코드

// _str = “1234”;

function checkNumbersOnly(_str) {​   

    if (_str == null || _str == “”) {

        return false;

    }

    _str = _str + “”;

    var len = _str.length;
    for (var i=0; i<len; i++) {
        var c = _str.charAt(i);
        if (c < ‘0’ || c > ‘9’) {
            return false;
        }
    }

    return true;

}

정규식 사용해서 문자열이 숫자로만 구성되어 있는지 체크

function checkNumbersOnly(_str) {

    if (_str == null || _str == “”) {

        return false;

    }

    _str = _str + “”;

    var regEx = /^[0-9]*$/;

    return regEx.test(_str);

}

SQLDeveloper 세션모니터 (대량 Insert / 대량 Update 수행시 진행률 체크)

SQLDeveloper 세션모니터 (대량 Insert / 대량 Update 수행시 진행률 체크)

DB에 대량 Insert 또는 대량 Update를 수행하고 있을 때 진행률(Progress Percentage)을 확인하고 싶을 때가 있다.

그럴 때 SQLDeveloper 의 세션모니터를 활용하면 된다.

1. SQLDeveloper – 상단메뉴의 도구(T) – 세션모니터(M) 선택

2. 세션이라는 탭이 나옴. 현재 수행되고 있는 작업 리스트가 나온다.

  여기서 원하는 row(예를 들면 대량 Update문)를 선택하면 하단에 다시 n개의 탭이 나온다.

3. 하단의 “긴 Opsq”탭 (제일 우측의 탭) 선택한 후 새로고침을 5 로 바꾸면, 진행률을 볼 수 있다.

[VB] 휴일제외하고 날짜 구하기. (수정)

[VB] 휴일제외하고 날짜 구하기.

첨부파일 있음.

—————————————-

Dim g_returnValue

Sub Macro1()

‘ Macro1 Macro

‘ 바로 가기 키: Ctrl+k

If ActiveCell.Offset(0, 0).Value = “” Then
    MsgBox (“현재 셀에 값이 존재하지 않습니다.”)
    Exit Sub
End If

If ActiveCell.Offset(0, -1).Value = “” Then
    MsgBox (“좌측 셀에 값이 존재하지 않습니다.”)
    Exit Sub
End If

If ActiveCell.Offset(0, -1).Value < 0 Then
    ActiveCell.Offset(0, 1).Value = ActiveCell.Offset(0, 0).Value
    ActiveCell.Offset(1, 0).Select
    Exit Sub
End If

Dim dt1 As Date
dt1 = ActiveCell.Offset(0, 0).Value

Dim plusNum
plusNum = ActiveCell.Offset(0, -1).Value

Dim holydayArray(10) As Date
holydayArray(0) = “2018-01-01”

Call addDateWithHoliday(dt1, plusNum, holydayArray)

ActiveCell.Offset(1, 0).Select

End Sub

Function addDateWithHoliday(orgDate, targetNum, holydayArray)
   
    Dim curNum
    curNum = 0
   
    Dim newDate As Date
    newDate = orgDate
   
    Dim yoil
   
    Dim limitLoop
    limitLoop = 300
   
    Dim curLoop
    curLoop = 0
   
    Do
        curLoop = curLoop + 1
        If curLoop > 100 Then
            Exit Do
        End If
       
        newDate = DateAdd(“d”, 1, newDate)
       
        ‘yoil = Right(newDate, 1)
        yoil = WeekdayName(Weekday(newDate))
        yoil = Left(yoil, 1)
        ‘MsgBox (yoil)
       
        Call hasValueInArray(newDate, holydayArray)
       
        If yoil = “토” Or yoil = “일” Then
           
        ElseIf g_returnValue = True Then
       
        Else
            curNum = curNum + 1
            If curNum >= targetNum Then
                Exit Do
            End If
        End If
    Loop
   
    ActiveCell.Offset(0, 1).Value = newDate
   
End Function

Function hasValueInArray(targetValue, targetArray)
    Dim count
    count = UBound(targetArray)
   
    g_returnValue = False
   
    For i = 0 To count
        If targetArray(i) = targetValue Then
            ‘일치
            g_returnValue = True
           
        Else
            ‘불일치
        End If
    Next i
   
End Function

 

javascript : 디버그를 위한 window.debug 함수

javascript : 디버그를 위한 window.debug 함수

window.debug = function() {
    var msg = “”;
    if (arguments.length > 0) {
        msg = arguments[0];
    }
 
    if (typeof (msg) != “string”) {
        alert(“데이터가 스트링 형태가 아닙니다.”);
        return false;
    }
 
    var viewWidth = 800;
    var viewHeight = 600;
    var viewTop = (window.screen.availHeight – viewHeight) / 2;
    var viewLeft = (window.screen.availWidth – viewWidth) / 2;
 
    var strOpenFeature = “width=” + viewWidth;
    strOpenFeature += “,height=” + viewHeight;
    strOpenFeature += “,left=” + viewLeft;
    strOpenFeature += “,top=” + viewTop;
    strOpenFeature += “,status=yes,resizable=yes,toolbar=no,menubar=no,location=no”;
 
    var childWinObj = window.open(“about:blank”, “_blank”, strOpenFeature);
 
    var oTextArea = childWinObj.document.createElement(“TEXTAREA”);
    oTextArea.style.width = “100%”;
    oTextArea.style.height = “100%”;
    oTextArea.value = msg;
 
    childWinObj.document.title = “DEBUG”;
    childWinObj.document.body.appendChild(oTextArea);
    childWinObj.document.body.style.padding = “0px”;
    childWinObj.document.body.style.margin = “0px”;
};

getTodayDateTime / getTodayDateTimeStd / getDateFormat

getTodayDateTime / getTodayDateTimeStd / getDateFormat

/**

* 현재날짜를 “yyyyMMddHHmmss” 형식의 String 형태로 리턴한다.

*/

public static String getTodayDateTime() {
  Calendar cal = Calendar.getInstance();
  StringBuffer today = new StringBuffer();
  today.append(String.format(“%04d”, cal.get(Calendar.YEAR)));
  today.append(String.format(“%02d”, cal.get(Calendar.MONTH) + 1));
  today.append(String.format(“%02d”, cal.get(Calendar.DAY_OF_MONTH)));
  today.append(String.format(“%02d”, cal.get(Calendar.HOUR_OF_DAY)));
  today.append(String.format(“%02d”, cal.get(Calendar.MINUTE)));
  today.append(String.format(“%02d”, cal.get(Calendar.SECOND)));
  return today.toString();
 }
 

/**

* 현재날짜를 “yyyy/MM/dd HH:mm:ss” 형식의 String 형태로 리턴한다.

*/
 public static String getTodayDateTimeStd() {
  Calendar cal = Calendar.getInstance();
  StringBuffer today = new StringBuffer();
  today.append(String.format(“%04d”, cal.get(Calendar.YEAR)));
  today.append(“/”);
  today.append(String.format(“%02d”, cal.get(Calendar.MONTH) + 1));
  today.append(“/”);
  today.append(String.format(“%02d”, cal.get(Calendar.DAY_OF_MONTH)));
  today.append(” “);
  today.append(String.format(“%02d”, cal.get(Calendar.HOUR_OF_DAY)));
  today.append(“:”);
  today.append(String.format(“%02d”, cal.get(Calendar.MINUTE)));
  today.append(“:”);
  today.append(String.format(“%02d”, cal.get(Calendar.SECOND)));
  return today.toString();
 }
 
 /**
  * Date 객체를 형식에 맞는 String 형태로 리턴한다.
  * format 예 : “yyyy/MM/dd”
  *
  * @param date
  * @param format
  * @return
  */
 public static String getDateFormat(Date date, String format) {
  return new java.text.SimpleDateFormat(format).format(date);
 }
 
 /**
  * Calendar 객체를 형식에 맞는 String 형태로 리턴한다.
  * format 예 : “yyyy/MM/dd”
  *
  * @param cal
  * @param format
  * @return
  */
 public static String getDateFormat(Calendar cal, String format) {
  return new java.text.SimpleDateFormat(format).format(castCalendarToDate((cal)));
 }
 
 /**
  * Calendar 객체를 Date 객체로 만들어 리턴한다.
  *
  * @param cal
  * @return
  */
 public static Date castCalendarToDate(Calendar cal) {
  return new java.sql.Date(cal.getTimeInMillis());
 }

getIndexOfSingleChar / getLastIndexOfSingleChar

getIndexOfSingleChar / getLastIndexOfSingleChar

    public static int getIndexOfSingleChar(String str, String singleCharToFind, int targetNum) {
        if (str == null || str.length() == 0) {
            return -1;
        }
       
        if (singleCharToFind == null || singleCharToFind.length() != 1) {
            // exception
            return -999;
        }
       
        int foundNum = 0;
       
        String tmpChar = “”;
        int len = str.length();
        for (int i=0; i<len; i++) {
            tmpChar = str.substring(i, i+1);
            if (tmpChar.equals(singleCharToFind)) {
                foundNum++;
                if (foundNum == targetNum) {
                    return i;
                }
            }
        }
       
        return -1;
    }
 

   
    public static int getLastIndexOfSingleChar(String str, String singleCharToFind, int targetNum) {
        if (str == null || str.length() == 0) {
            return -1;
        }
       
        if (singleCharToFind == null || singleCharToFind.length() != 1) {
            // exception
            return -999;
        }
       
        int foundNum = 0;
       
        String tmpChar = “”;
        int len = str.length();
        int lastIdx = len – 1;
        for (int i=lastIdx; i>-1; i–) {
            tmpChar = str.substring(i, i+1);
            if (tmpChar.equals(singleCharToFind)) {
                foundNum++;
                if (foundNum == targetNum) {
                    return i;
                }
            }
        }
       
        return -1;
    } 

오라클 임포트하는 방법 (oracle import / impdp) (171224수정)

오라클 임포트하는 방법 (oracle import / impdp) (171224수정)

작업 전 덤프 파일(확장자 dmp)을 준비해놓는다. 덤프는 expdp 로 한다. exp 가 아니라 expdp 로 해야 한다. (exp 로 할 경우 row개수 0인 테이블 가져오지 못하는 경우 발생했음)

— DATA_PUMP_DIR 안에 덤프파일 (ex: ose36dev.dmp)을 넣어놓는다.
— 예를 들면 C:\app\ora11\admin\orcl\dpdump 가 DATA_PUMP_DIR 위치이다.
— 정확한 DATA_PUMP_DIR 경로는 SqlDeveloper에서 알아낼 수 있다.
— SqlDeveloper 기동 – 상단메뉴의 “보기” – “DBA” 선택하면 좌측 하단에 DBA 창이 나온다. 특정 데이터베이스 접속 후, “데이터펌프” 항목 위에서 마우스 우클릭, “데이터 펌프 임포트 마법사” 선택, “다음” 누르고 잠시 기다리면, 경고창에 <메타 데이터 가져오기를 실패했습니다. 예외 사항: ORA-31640: 읽기를 위해 덤프 파일 “C:\app\ora11\admin\orcl\dpdump\EXPDAT01.DMP”을(를) 열 수 없음> 이라고 나온다. 여기에 쓰여있는 경로가 DATA_PUMP_DIR 이다.

— 오라클 아이디와 비번 아래와 같을 경우.
sytem/oracle

— 테이블 스페이스 차지하는 공간 조회
select * from sys.dba_data_files;

— 남은 공간 조회
select * from sys.dba_free_space;

— 데이터 펌프에서 쓰는 alias 들을 얻는다.
select * from dba_directories;

— 기존 테이블 스페이스 삭제한다. (기존에 생성되어 있지 않다면 이 과정은 생략해도 된다) 예를 들면 아래와 같다.
drop tablespace XFUSERS INCLUDING CONTENTS;
drop tablespace XFBRMS INCLUDING CONTENTS;
drop tablespace XFIDX INCLUDING CONTENTS;

— 리맵(remap)하기 위해, xf_users 와 xf_index 는 만들지 않는다. 리맵(remap)이란 테이블스페이스를 한 곳으로 몰아주는 것을 의미한다. (cf) remap_tablespace=xf_users:xfusers,xf_index:xfidx
— drop tablespace XF_USERS INCLUDING CONTENTS;
— drop tablespace XF_INDEX INCLUDING CONTENTS;

— 테이블 스페이스 새로 잡는다. 예를 들면 아래와 같다.
create tablespace XFUSERS datafile ‘C:\app\ora11\oradata\orcl\xfusers.dbf’ size 4096M;
create tablespace XFBRMS datafile ‘C:\app\ora11\oradata\orcl\xfbrms.dbf’ size 4096M;
create tablespace XFIDX datafile ‘C:\app\ora11\oradata\orcl\xfidx.dbf’ size 512M;

— 만약, 테이블 스페이스 변경하고 싶으면 아래와 같이 한다. (변경하고 싶지 않다면 생략)
— alter tablespace XFUSERS add datafile ‘C:\app\ora11\oradata\orcl\xfusers.dbf’ size 4096M;

— 유저 삭제
drop user ose36dev cascade;

— 유저 생성
grant connect to ose36dev identified by ose36dev;
alter user ose36dev default tablespace XF_USERS temporary tablespace TEMP;
grant resource to ose36dev;

— 임포트 하기 (cmd 창에서 수행한다)
— 리맵(remap)하여 임포트한다.
— DATA_PUMP_DIR 은 C:\app\ora11\admin\orcl\dpdump 을 가리킨다.
— 참고로 리맵(remap)하지 않으려면 명령어 뒤부분의 remap_tablespace 항목을 제거하면 된다.
impdp system/oracle dumpfile=ose36dev.dmp full=y directory=DATA_PUMP_DIR logfile=xf.log job_name=xfjob remap_tablespace=xf_users:xfusers,xf_index:xfidx

임포트가 완료될때까지 기다린다.

끝.

+ 문제해결
— (문제 생겼을 경우) 임포트 중인 job 죽이기
impdp system/oracle attach=xfjob

kill_job

이 작업을 정지하시겠습니까?([yes]:no)
yes

하면 정지된다. 처음부터 다시 시도하면 된다.

github.io 에서 한글 깨지는 현상

github.io 에서 한글 깨지는 현상

github.io 에서 한글 깨짐 현상이 있었다. 참고로 내가 생성/운영하는 웹페이지가 아니고, 모든 [고유아이디].github.io 에서 깨지는 현상이었다.

첨부한 스크린샷과 같이 한글이 깨지고 있었다. 예를 들어,

ex) 내가 개인적으로 무척 좋아하는 문구가 있다. Real Artists Ship. 스티브잡스가 한 말이라고 알려져 있는데 굳이 해석하자면 진정한 예술가는 출시한다 정도로 해석할 수 있을까?

-> 내가!개인적으로!무척!좋아하는!문구가!있다/Sf bm!Bsujt ul !Ti jq 스티브잡스가!한!말이라고!알려져있는데-!구지!해석하자면!지정한!예술가는!출시한다/정도로!해석할!수!있을까@

이렇게 깨진다.

구글에서 이리저리 검색해보았지만 방법을 몰랐다. 막상 내용을 복사 – 메모장에서 붙여넣기 해보면 깨지지 않고 잘 나오는 것으로 보아 IE 브라우저 문제로 보였다. (크롬에서는 정상적으로 보인다)

참고1) 한글 깨짐 화면

참고2) 한글 깨짐 화면 2

이러한 내용을 페이스북 페이지 <코딩이랑 무관합니다만,>에 문의 결과, 어떤 분께서 폰트 문제일 것 같다는 의견을 주셨다.

이에 따라 개발자도구에서 body 영역의 font를 Helvetica Neue 에서 굴림 으로 변경하였더니 (임시로나마) 해결됐다.

main.css의 내용을 보니 font Helvetica Neue, Helvetica, Arial, sans-serif 순으로 되어 있다. 사실 github.io 사이트 탓이라기보다 내 컴퓨터 폰트 문제인 것 같다. 폰트가 다 없으면 Arial이라도 바라봐야할텐데… 개발자 도구로 몇 가지 테스트를 해보니 Helvetica 를 바라보고 있다. 하필 Helvetica 폰트만 문제이고, 폰트 재설치를 해도 안된다. 여튼 github.io 사이트 탓은 아닌 것이다!

참고 스크린샷을 첨부한다. 끝.

참고3) 문제해결 (클릭하면 크게 보임)


오라클 시퀀스 한꺼번에 증가시키기 (sequence 증가)

오라클 시퀀스 한꺼번에 증가시키기 (sequence 증가)

시퀀스 현재값 조회

select seq_bbs_article.currval from dual;

시퀀스값 1씩 증가시키기

select seq_bbs_article.nextval from dual;

시퀀스값 한꺼번에 증가시키기

— 특정값으로 맞추고 싶을 때, 증가분을 늘려준다.

alter sequence seq_bbs_article increment by 5000;
select seq_bbs_article.nextval from dual;
alter sequence seq_bbs_article increment by 1;

javascript reviseDestFilePath (로컬에 저장할 파일패스 값을 보정)

javascript reviseDestFilePath (로컬에 저장할 파일패스 값을 보정)

/**
 * 로컬에 저장할 파일패스 값을 보정한다.
 */
function reviseDestFilePath(str) {
 if (str == null || str == “”) {
  return “”;
 }
 
 // 파일에는 다음 문자를 사용할 수 없습니다.
 str = replaceAllStr(str, “\\”, “”);
 str = replaceAllStr(str, “/”, “”);
 str = replaceAllStr(str, “:”, “”);
 str = replaceAllStr(str, “*”, “”);
 str = replaceAllStr(str, “?”, “”);
 str = replaceAllStr(str, “\””, “”);
 str = replaceAllStr(str, “<“, “”);
 str = replaceAllStr(str, “>”, “”);
 str = replaceAllStr(str, “|”, “”);
 
 // 작은따옴표는 대상이 아니지만 일관성을 위해 쌍따옴표처럼 처리한다.
 str = replaceAllStr(str, “‘”, “”);
 
 // 빈칸은 언더바로 채운다.
 str = replaceAllStr(str, ” “, “_”);
 return str;
}

/**
 * 정규식 사용하지 않는 자바스크립트 replaceAll
 */
function replaceAllStr(str, s1, s2) {
 if (str == null || str == “”) {
  return “”;
 }
 
 if (s1 == null || s1 == “”) {
  return str;
 }
 
 while (str.indexOf(s1) > -1) {
  str = str.replace(s1, s2);
 }
 return str;
}
 

파일패스의 확장자 치환

파일패스의 확장자 치환

기본적인 내용이지만 짜증나서 작성함.

javasciprt 에서 확장자 .bin을 .html로 바꿔야 한다면 보통 아래와 같이 쓸 것이다.

이때 주의할 것은, 정규식의 점(.)은 모든 문자를 의미하므로 앞에 역슬래시를 붙여줘야 한다는 것이다.

var basicPath = “기본파일패스”;

basicPath= basicPath.replace(/\.bin/g, “.html”);

하지만 상단의 코드보다 하단의 코드를 권장한다.

이유는 패스상에 .bin이 하나 더 포함될 수도 있고, 정규식에서 역슬래시를 쓰지 않고 점(.)만 사용할 경우 abin, bbin, cbin, …, zbin 등 다 바뀌어버리는 부작용이 일어날 수 있기 때문이다. (매우 흔하게 발견되는 사례임)

확장자는 제일 끝에 1번만 위치하는게 정상이므로, 바꿔줄 때도 lastIndexOf를 이용해 마지막 부분만 갈아끼워주는게 정석이다. 제발.

var basicPath = “기본파일패스”;
var lastIdx = basicPath.lastIndexOf(“.bin”);
if (lastIdx > -1) {
    basicPath = basicPath.substring(0, lastIdx) + “.html”;
}

현재 사용중인 Windows 시디키 알아내는 프로그램(ProduKey)

현재 사용중인 Windows 시디키 알아내는 프로그램(ProduKey)

NirSoft의 ProduKey 라는 프로그램이 있다. Windows CD Key는 물론 Microsoft Office CD Key도 알아낼 수 있다.

웹페이지 링크 : http://www.nirsoft.net/utils/product_cd_key_viewer.html

다운로드 링크 : http://www.nirsoft.net/utils/produkey.zip

한글패치 : http://www.nirsoft.net/utils/trans/produkey_hangul.zip

혹시나 몰라 한글패치를 적용한 1.90 버전을 묶어 첨부한다.

remove indexes on Vector / ArrayList

remove indexes on arraylist, vector

IS-A 혹은 HAS-A 고려하여 멤버함수로 만든 것.

/**
  * 인덱스 숫자 리스트를 넘겨서 일괄 지운다.
  * @param removeIdxList
  */
 public void remove(Vector removeIdxList, LogWriter _logger) {
  // 삭제할 인덱스 리스트 비었을 경우 그만둔다.
  if (removeIdxList == null || removeIdxList.size() == 0) {
   return;
  }
  
  // 삭제할 인덱스 리스트 정렬
  Collections.sort(removeIdxList);
  
  int idxPreDel = -1;
  
  int count = removeIdxList.size();
  int idx = 0;
  int lastIdx = count – 1;
  for (int i=lastIdx; i>=0; i–) {
   idx = (Integer) removeIdxList.get(i);
   if (idxPreDel != idx) {
    list.remove(idx);
    idxPreDel = idx;
   } else {
    // 이미 지운 인덱스는 스킵한다.
    // 한 번 지운 인덱스를 또 지우면 해당 번호 이후로 모두 한 칸씩 땡겨지게 되는 치명적 버그가 생긴다.
    if (_logger != null) {
     _logger.error(“remove : 이미 지운 인덱스 번호는 스킵한다. [” + idx + “] 번”);
    }
   }
  }
 }

인트벡터맵 (IntVectorMap)

인트벡터맵 (IntVectorMap)

public class IntVectorMap {
 private HashMap innerMap = new HashMap();
 private final int NULL_INT_VALUE = -1;
 
 public void putToVector(String key, int value) {
  if (key == null || key.length() == 0) {
   return;
  }
  
  key = key.toUpperCase();
  
  // 키가 존재하면 벡터를 추가한다.
  if (innerMap.get(key) != null) {
   ((Vector) innerMap.get(key)).add(value);
   
  } else {
   
   //키가 존재하지 않으면 벡터 생성.
   Vector newVector = new Vector();
   newVector.add(value);
   innerMap.put(key, newVector);
  }
 }
 
 public int getVectorSize(String key) {
  Vector vector = getVector(key);
  if (vector == null || vector.size() == 0) {
   return 0;
  } else {
   return vector.size();
  }
 }
 
 public Vector getVector(String key) {
  if (key == null || key.length() == 0) {
   return null;
  }
  
  key = key.toUpperCase();
  
  if (innerMap.get(key) == null) {
   return null;
  }
  
  Vector vector = (Vector) innerMap.get(key);
  if (vector == null || vector.size() == 0) {
   return null;
  } else {
   return vector;
  }
 }
 
 public int getVectorValue(String key, int index) {
  if (key == null || key.length() == 0) {
   return NULL_INT_VALUE;
  }
  
  key = key.toUpperCase();
  
  if (innerMap.get(key) == null) {
   return NULL_INT_VALUE;
  }
  
  Vector vector = (Vector) innerMap.get(key);
  if (vector == null || vector.size() == 0) {
   return NULL_INT_VALUE;
  }
  
  int lastIdx = vector.size() – 1;
  if (lastIdx < index) {
   return NULL_INT_VALUE;
  }
  
  return Integer.parseInt(String.valueOf(vector.get(index)));
 }
}
 

스트링벡터맵 (StringVectorMap)

스트링벡터맵 (StringVectorMap)

public class StringVectorMap {
 private HashMap innerMap = new HashMap();
 
 public void putToVector(String key, String value) {
  if (key == null || key.length() == 0) {
   return;
  }
  
  if (value == null) {
   value = “”;
  }
  
  key = key.toUpperCase();
  
  // 키가 존재하면 벡터를 추가한다.
  if (innerMap.get(key) != null) {
   ((Vector) innerMap.get(key)).add(value);
   
  } else {
   
   //키가 존재하지 않으면 벡터 생성.
   Vector newVector = new Vector();
   newVector.add(value);
   innerMap.put(key, newVector);
  }
 }
 
 public int getVectorSize(String key) {
  Vector vector = getVector(key);
  if (vector == null || vector.size() == 0) {
   return 0;
  } else {
   return vector.size();
  }
 }
 
 public Vector getVector(String key) {
  if (key == null || key.length() == 0) {
   return null;
  }
  
  key = key.toUpperCase();
  
  if (innerMap.get(key) != null) {
   Vector vector = (Vector) innerMap.get(key);
   if (vector == null || vector.size() == 0) {
    return null;
   } else {
    return vector;
   }
   
  } else {
   return null;
  }
 }
 
 
}

url2Html : url에 해당하는 html 코드를 스트링 형태로 리턴

url2Html : url에 해당하는 html 코드를 스트링 형태로 리턴

/**
  * url에 해당하는 html 코드를 스트링 형태로 리턴한다.
  *
  * @param url
  * @param encode
  * @return
  * @throws Exception
  */
 public static String url2Html(String url, String encode) throws Exception {
  URL urlObj = null;
  BufferedReader in = null;
  String line = null;
  
  StringBuffer buff = new StringBuffer();
  
  try {
   if (encode == null || encode.length() == 0) {
    encode = “UTF-8”;
   }
   
   urlObj = new URL(url);
   in = new BufferedReader(new InputStreamReader(urlObj.openStream(), encode));
   
   while ((line = in.readLine()) != null) {
    buff.append(line);
   }
       
  } catch (Exception e) {
   _logger.exception(e);
   throw e;
   
  } finally {
   try {
    if (in != null) {
     in.close();
    }
   } catch (Exception e2) {
   } finally {
    in = null;
   }
  }
  
  return buff.toString();
 }

자바 메서드 : 에러가 나지 않는 서브스트링

자바 메서드 : 에러가 나지 않는 서브스트링

길이 초과 등을 방지한다.

 /**
  * 에러가 나지 않는 서브스트링 : 길이 초과를 방지한다.
  *
  * @param str
  * @param beginIdx
  * @param endIdxNotIncluded
  * @return
  */
 private String substringNotError(String str, int beginIdx, int endIdxNotIncluded) {
  if (str == null || str.length() == 0) {
   return “”;
  }

  if (beginIdx < 0) {
   beginIdx = 0;
  }
  
  if (beginIdx >= endIdxNotIncluded) {
   return “”;
  }
  
  if (endIdxNotIncluded > str.length()) {
   endIdxNotIncluded = str.length();
  }
  
  return str.substring(beginIdx, endIdxNotIncluded);
 }
 
 /**
  * 에러가 나지 않는 서브스트링 (오버로딩) : 길이 초과를 방지한다.
  *
  * @param str
  * @param beginIdx
  * @return
  */
 private String substringNotError(String str, int beginIdx) {
  if (str == null || str.length() == 0) {
   return “”;
  }
  
  return substringNotError(str, beginIdx, str.length());
 }

톰캣 펌사이즈 설정 (tomcat permsize) – java.lang.OutOfMemoryError

톰캣 펌사이즈 설정 (tomcat permsize) – java.lang.OutOfMemoryError

java.lang.OutOfMemoryError : PermGen space 에러발생시 톰캣 펌사이즈 설정해줘야 한다.

1. vi 로 톰캣/bin/catalina.sh 을 수정한다.

2. 적당한 위치에 아래 내용을 넣는다.

set “CATALINA_OPTS=-Xms3072M -Xmx3072M -XX:PermSize=512M -XX:MaxPermSize=512M”

아래 스크린샷 참조.

 

자바 메서드 : 더 나중 날짜인지 체크

자바 메서드 : 더 나중 날짜인지 체크

// getLaterTimeString : 더 나중 날짜 가져오기

private String getLaterTimeString(String oldTime, String newTime) {
  
  try {
   if (oldTime == null || oldTime.length() == 0) {
    if (newTime == null || newTime.length() == 0) {
     return “”;
    } else {
     return newTime;
    }
   }
   
   if (newTime == null || newTime.length() == 0) {
    return oldTime;
   }
   
   String oldTrimed = oldTime.replace(” “, “”).trim();
   String newTrimed = newTime.replace(” “, “”).trim();
   
   int oldFront = Integer.parseInt(oldTrimed.substring(0, 8));
   int newFront = Integer.parseInt(newTrimed.substring(0, 8));
   
   if (oldFront > newFront) {
    return oldTime;
    
   } else if (oldFront < newFront) {
    return newTime;
    
   } else {
    
    int oldBack = Integer.parseInt(oldTrimed.substring(8));
    int newBack = Integer.parseInt(newTrimed.substring(8));
    
    if (oldBack > newBack) {
     return oldTime;
     
    } else if (oldBack < newBack) {
     return newTime;
    }
   }
     
  } catch (Exception e) {
   _logger.error(“getLaterTimeString Error : ” + e.getMessage());
   _logger.exception(e);
   return oldTime;
  }
  
  return oldTime;
 }

————————————————–

// newTimeIsLater: 뒤의 Time이 더 나중 날짜인지 체크

// newTime(두번째 아규먼트)이 oldTime(첫번째 아규먼트)보다 나중인지 검사. 비교 불가이거나 에러 발생시 null 을 리턴한다.
private Boolean newTimeIsLater(String oldTime, String newTime) {
  
  try {
   if (oldTime == null || oldTime.length() == 0) {
    if (newTime == null || newTime.length() == 0) {
     return null;
    } else {
     return true;
    }
   }
   
   if (newTime == null || newTime.length() == 0) {
    return false;
   }
   
   String oldTrimed = oldTime.replace(” “, “”).trim();
   String newTrimed = newTime.replace(” “, “”).trim();
   
   int oldFront = Integer.parseInt(oldTrimed.substring(0, 8));
   int newFront = Integer.parseInt(newTrimed.substring(0, 8));
   
   if (oldFront > newFront) {
    return false;
    
   } else if (oldFront < newFront) {
    return true;
    
   } else {
    
    int oldBack = Integer.parseInt(oldTrimed.substring(8));
    int newBack = Integer.parseInt(newTrimed.substring(8));
    
    if (oldBack > newBack) {
     return false;
     
    } else if (oldBack < newBack) {
     return true;
    }
   }
     
  } catch (Exception e) {
   _logger.error(“newTimeIsLater Error : ” + e.getMessage());
   _logger.exception(e);
   return null;
  }
  
  return null;
 }

————————————————–

DB로부터 길이가 같은 날짜(20XX0000 000000) 2개를 SELECT 해서 비교한다는 전제하에 만들었음.

DB에 값이 잘못들어가 있을 확률이 존재한다면, 예를 들어 8자리 미만의 값이 섞여 들어가 있다면 substring에서 오류가 날것임.

DB를 신뢰할 수 없다면 substring 직전에 길이 비교를 선행해서 길이가 큰 쪽이 크다고 판단내리고,

로그에 길이가 다르다고 찍어주는 쪽으로 보완하는게 안전할 듯.

cf) substring을 에러가 나지 않는 substring 으로 대체하면 매우 안전하다.

참고url : http://blog.naver.com/bb_/221066653530

sapjco3.jar / sapjco3.dll / libsapjco3.so

sapjco3.jar / sapjco3.dll / libsapjco3.so

sapjco3.jar / sapjco3.dll / libsapjco3.so 는 SAP 연동시 필요한, SAP Java Connector(JCO) 관련 파일들이다.

Window 이클립스 로컬 세팅의 경우 sapjco3.jar 는 Java Build Path 메뉴에서 라이브러리 추가하고, sapjco3.dll 는 System32에 넣는다.

Windows 서버의 경우 sapjco3.jar 파일과 sapjco3.dll 파일을 Apache Tomcat의 lib 폴더에 넣는다.

Linux 서버의 경우 sapjco3.jar 파일과 libsapjco3.so 파일을 Apache Tomcat의 lib 폴더에 넣는다.

서버별 자세한 구분은 아래와 같다.

IBM AIX 서버: libsapjco3.so, sapjco3.jar
HP-UX11 서버 : libsapjco3.sl/so, sapjco3.jar
Linux  서버 : libsapjco3.so, sapjco3.jar
Oracle Solaris 서버 : libsapjco3.so, sapjco3.jar
Microsoft Windows 서버 : sapjco3.dll, sapjco3.jar

IBM iSeries 서버 : libicudata34.a, libicui18n34.a, libicuuc34.a, libsapjco3.so, os4apilib.so, sapjco3.jar

참고 페이지 1 : https://help.sap.com/doc/00f68c2e08b941f081002fd3691d86a7/release/ko-KR/ffe3b941e82641bc97b76ce522423d83.html

참고 페이지 2 : https://www.ibm.com/support/knowledgecenter/en/SS3JSW_5.2.0/com.ibm.help.svcs_adpts_m_z.doc/sapsuiteadapterforjco3x_521/downloadingthesapjavaconnector.html

VPN 연결 후 특정사이트 접속 안될 경우 (route add)

VPN 연결 후 특정사이트 접속 안될 경우 (route add)

VPN 연결하면 VPN에서 정한 게이트웨이만 통하기 때문에, 기존에 접속되던 사이트가 접속되지 않을 수 있다.

1. cmd 연다

2. ipconfig /all 입력한다.

3. 여러 아이피 중에서 내 아이피 찾아서 설명(명칭)과 기본 게이트웨이 기억한다. 예를 들어 아래 스크린샷처럼 내 아이피를 찾았다면, Realtek PCIe GBE Family Controller 를 기억하면 된다.

4. route print 입력해서, 인터페이스 목록을 본다. 좌측에 나온 번호를 기억하자. 아래 스크린샷에서는 3번이다.

5. 이제 cmd 를 관리자권한으로 실행한다.

6. route add 명령어로 아래처럼 입력하면 ‘확인!’이 나오며 설정된다.

route add [특정아이피] MASK [서브넷마스크] [기본게이트웨이] IF 번호 -p

특정아이피에 접속하려고 시도할 때 기본게이트웨이를 통해서 나가게 해주는 명령이다. 이제 vpn에 연결해도 연결하지 않았을 때와 마찬가지로 동작할 것이다.

예를 들어, VPN 연결 후 구글 접속이 되지 않는다고 해보자.

그럼 cmd에 ping -t google.com 을 쳐서 아이피 주소를 알아낸다.

보다시피 구글 닷컴의 아이피주소는 216.58.221.238 이다.

VPN을 연결해도 접속해보고 싶다면 아래와 같이 입력한다. (기본게이트웨이가 100.200.50.50 인 경우 가정)

ex 0 ) route add 216.58.221.238 MASK 255.255.255.255  100.200.50.50 IF 3 -p

ex 1 ) route add 216.58.221.0 MASK 255.255.255.0  100.200.50.50 IF 3 -p

ex 2 ) route add 216.58.0.0 MASK 255.255.0.0  100.200.50.50 IF 3 -p

-p 옵션을 주면 영구 적용된다. 적용결과는 route print 로 볼 수 있다.

적용해제하려면

route delete 216.58.221.238 IF 3 -p 하면 된다.

자바 수행시간 / 자바 실행시간

자바 수행시간 / 자바 실행시간

long beginTime = System.currentTimeMillis();
   
/*

시간이 걸리는 로직

로직

로직

*/
   
// 수행시간 계산
long endTime = System.currentTimeMillis();
long ms = endTime – beginTime;
float sec = (float) ms / 1000;
float min = (float) sec / 60;

if (min > 1) {
    System.out.println(“수행시간: ” + min + “(min)”);
    
} else if (sec > 1) {
    System.out.println(“수행시간: ” + sec + “(sec)”);
    
} else {
    System.out.println(“수행시간: ” + ms + “(ms)”);
}

오라클 홑따옴표(Single Quote) 사용

오라클 홑따옴표(Single Quote) 사용

SELECT Q'[홑따옴표가포함된문자열]’ FROM DUAL;

ex) SELECT Q'[Paddy O’Reilly]’ FROM DUAL;

=> Paddy O’Reilly

———-

2020.04.21 내용 추가.

지금 보니 위와 같이 쓸 필요가 없다.

그냥 홑따옴표를 두 번 적어주면 된다.

ex) SELECT ‘Paddy O”Reilly’ FROM DUAL;

=> Paddy O’Reilly

윈도우 시작프로그램 삭제

윈도우 시작프로그램 삭제

시작 – 실행 – regedit

[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run]
[HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\Windows\CurrentVersion\Run]
[HKEY_CURRENT_USER\Software\Microsoft\Windows\CurrentVersion\Run]

위 3군데에서 찾아서 지우면 된다.

[Javascript] encodeURI 와 encodeURIComponent 차이

[Javascript] encodeURI 와 encodeURIComponent 차이

1. encodeURI

encodeURI 는 주소 전체를 인코딩할 때 사용한다. : ; / = ? & 등 특수문자는 인코딩하지 않는다. (파라미터가 붙는 후반부를 손상시킬 수 있으므로)

2. encodeURIComponent

encodeURIComponent 는 개별 파라미터를 인코딩할 때 사용한다.  : ;. / = ? & 등 특수문자까지 인코딩한다.

javascript trim / javascript replaceAll

javascript trim / javascript replaceAll

String.prototype.replaceAll = function(org,dest) {
    return this.split(org).join(dest);
}

String.prototype.trim = function() {
    return this.replace(/(^\s*)|(\s*$)/g, “”);
}

프로토타입을 변경하고 싶지 않다면 아래와 같이 써도 되겠다.

function replaceAllString(str, org, dest) {
    return str.split(org).join(dest);
}

function trimString(str) {
    return str.replace(/(^\s*)|(\s*$)/g, “”);
}

문자열 관련 javascript function 모음

문자열 관련 javascript function 모음

코딩하다 발견해서 올려봄. 문자열 관련 유틸인듯. 자세히 보진 않았지만 멋지네.

   String.prototype.trim = function() {
      return this.replace(/(^\s*)|(\s*$)|($\s*)/g, “”);
   }

   String.prototype.isid = function() {
      if (this.search(/[^A-Za-z0-9_-]/) == -1)
         return true;
      else
         return false;
   }

   String.prototype.isalpha = function() {
      if (this.search(/[^A-Za-z]/) == -1)
         return true;
      else
         return false;
   }

   String.prototype.isnumber = function() {
      if (this.search(/[^0-9]/) == -1)
         return true;
      else
         return false;
   }

   String.prototype.isemail = function() {
      var flag, md, pd, i;
      var str;

      if ( (md = this.indexOf(“@”)) < 0 )
         return false;
      else if ( md == 0 )
         return false;
      else if (this.substring(0, md).search(/[^.A-Za-z0-9_-]/) != -1)
         return false;
      else if ( (pd = this.indexOf(“.”)) < 0 )
         return false;
      else if ( (pd + 1 )== this.length || (pd – 1) == md )
         return false;
      else if (this.substring(md+1, this.length).search(/[^.A-Za-z0-9_-]/) != -1)
         return false;
      else
         return true;
   }

   String.prototype.korlen = function() {
      var temp;
      var set = 0;
      var mycount = 0;
     
      for( k = 0 ; k < this.length ; k++ ){
         temp = this.charAt(k);
     
         if( escape(temp).length > 4 ) {
            mycount += 2;
         }
         else mycount++;
      }

      return mycount;
   }

   String.prototype.isssn = function() {
     
      var first  = new Array(6);
      var second = new Array(7);
      var total = 0;
      var tmp = 0;
     
      if ( this.length != 13 )
         return false;
      else {
         for ( i = 1 ; i < 7 ; i++ )
            first[i] = this.substring(i – 1, i);
     
         for ( i = 1 ; i < 8 ; i++ )
            second[i] = this.substring(6 + i – 1, i + 6);
     
         for ( i = 1 ; i < 7 ; i++ ) {
            if ( i < 3 )
               tmp = Number( second[i] ) * ( i + 7 );
            else if ( i >= 3 )
               tmp = Number( second[i] ) * ( ( i + 9 ) % 10 );
        
            total = total + Number( first[i] ) * ( i + 1 ) + tmp;
         }
     
         if ( Number( second[7] ) != ((11 – ( total % 11 ) ) % 10 ) )
            return false;
      }
      return true;
   }

 String.prototype.ByteLength = function() {
  var i,ch;
  var strLength = this.length;
  var count = 0;

  for(i=0;i<strLength;i++)
  {
   ch = escape(this.charAt(i));

   if(ch.length > 4)
    count += 2;
   else if(ch!=’\r’)
    count++;
  }
  return count;
 }
/**
 * 문자열을 형식화(3자리마다 콤마 삽입)된 식으로 반환합니다.
 */
String.prototype.NumberFormat = function() {
 var str = this.replace(/,/g,””);
 var strLength = str.length;

 if (strLength<=3) return str;
 
    var strOutput = “”;
    var mod = 3 – (strLength % 3);
 var i;

    for (i=0; i<strLength; i++)
 {
  strOutput+=str.charAt(i);
        if (i < strLength – 1)
  {
   mod++;
            if ((mod % 3) == 0)
   {
    strOutput +=”,”;
                mod = 0;
   }
  }
 }
 return strOutput;
}

/**
 * 3자리수마다 “,”처리 삭제 wookki25 2005-09-21
 */
String.prototype.DeNumberFormat = function() {
 var str = this.replace(/,/g,””);
 return str;
}

String.prototype.isKorean = function() {
 Unicode = this.charCodeAt(0);
 if ( !(44032 <= Unicode && Unicode <= 55203) )
  return false;
 else
  return true;
}
String.prototype.isEnglish = function() {
 if (this.search(/[^A-Za-z]/) == -1)
    return true;
 else
    return false;
}

String.prototype.lenH = function() {
   var temp;
   var set = 0;
   var mycount = 0;
     
   for( k = 0 ; k < this.length ; k++ ){
      temp = this.charAt(k);
     
      if( escape(temp).length > 4 ) {
         mycount += 2;
      }
      else mycount++;
   }

   return mycount;
}

/**
 *  문자열에서 Byte Len만큼 문자열 잘라온다.
 *  ex) var s = MidH(“대한민국만세123ABS”,0,10);
 */
function MidH(str, n, len){
 //n : 시작점;
 //len : ByteLength;
 var ret=””;
 var tmp = str.substring(n, str.length);
 var s=””;
 var j=0;
 if(tmp.length>0){
  for(i=0; i< tmp.length; i++){
   if(j <= len){
    s = tmp.substring(i,i+1);
    if(s.lenH()==2){
     j = j+2;
    }else{
     j++;
    }
    ret = ret + s;
   }else{
    break;
   }
  }
 }
 return ret;
}

/**
 *  문자열이 존재하는지 체크합니다. (복수 가능)
 *  ex) StrMultiExists(id,”sysadmin”,”admin”…);
 */
function StrMultiExists(str)
{
 var i;
 var argCount = arguments.length;
 if (argCount==0) return false;
 var regStr = “”;

 for(i=1; i<argCount; i++) {
  regStr+=”(“+arguments[i].replace(/([\^\\\$\*\+\?\.])/g,”\\$1“)+”)|”;
 }

 if (str.search(eval(“/”+regStr.replace(/\|$/g,””)+”/g”))==-1) return false;
 else return true;
}

/**
 *  문자열을 특정 문자열을 나눠 배열형태의 값으로 반환합니다.
 */
function StringTokenizer(str,separator) {
 arrayOfStrings = str.split(separator);
 return arrayOfStrings;
}

/**
 * 올바른 메일형식인지 체크합니다.
 */
function isValidEmail(str) {
 var re=new RegExp(“^[_a-z0-9-]+(\.[_a-z0-9-]+)*@[a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3})$”,”gi”);
 if (str.match(re)) return true;
 else return false;
}

/**
 * 올바른 홈페이지형식인지 체크합니다.
 */
function isValidHomepage(str) {
 var re=new RegExp(“^((ht|f)tp:\/\/)((([a-z0-9-]+(\.[a-z0-9-]+)*(\.[a-z]{2,3}))|(([0-9]{1,3}\.){3}([0-9]{1,3})))((\/|\\?)[a-z0-9~#%&’_\+=:\?\.-]*)*)$”,”gi”);
 if (str.match(re)) return true;
 else return false;
}

/**
 * 올바른 주민등록번호인지 체크합니다.
 */
function IsResidentRegistrationNumber(str1, str2) {
 if (isNull(str1)) return false;
 if (isNull(str2)) return false;

 var Sum = 0;
 var i, j;

 for (i=0, j=2 ; i<=5 ;i++,j++)
 {
  Sum += parseInt(str1.charAt(i)) * j;
 }

 for (i=0, j=8;i<=5;i++, j++)
 {
  Sum += parseInt(str2.charAt(i)) * j;
  if (j==9) j=1;
 }

 if ((Sum=11-(Sum%11))>9) Sum=Sum%10;

 if (parseInt(str2.charAt(6)) != Sum) return false;
 return true;
}

/**
 * 알파벳만으로 구성된 문자열인지 체크합니다.
 */
function isAlphabet(str) {
 if (str.search(/[^a-zA-Z]/g)==-1) return true;
 else return false;
}

/**
 * 한글로만 구성된 문자열인지 체크합니다.
 */
function isKorean(str) {
 var strLength = str.length;
 var i;
 var Unicode;

 for (i=0;i<strLength;i++) {
  Unicode = str.charCodeAt(i);
  if ( !(44032 <= Unicode && Unicode <= 55203) ) return false;
 }
 return true;
}

/**
 * 숫자만으로 구성된 문자열인지 체크합니다.
 */
function isDigit(str) {
 if (str.search(/[^0-9]/g)==-1) return true;
 else return false;
}

/**
 * 문자열이 NULL인지 체크합니다.
 */
function isNull(str) {
    if (str == null || str == “”) return true;
    else return false;
}

/**
 * 문자열에 한칸이상의 스페이스 입력이 있는지를 체크합니다.
 */
function isValidSpace(str) {
 if (str.search(/[\s]{2,}/g)!=-1) return false;
 else return true;
}

/**
 * 팝업창의 위치값을 마지막에 받아서 창을 띄웁니다.
 */
function Main_OpenWindow(OpenFile,name,nWidth,nHeight,ScrollYesNo,ResizeYesNo,TPosition,LPosition)
{
 var newWin = window.open(OpenFile,name,”width=”+nWidth+”,height=”+nHeight+”,toolbar=no,directories=no,status=no,scrollbars=”+ ScrollYesNo +”,resizable=”+ ResizeYesNo +”,menubar=no,Top=”+ TPosition +”,left=”+ LPosition);
 newWin.focus();
}

function OpenWindow(OpenFile,name,nWidth,nHeight,ScrollYesNo,ResizeYesNo) {
 var newWin
 newWin = window.open(OpenFile,name,”width=”+nWidth+”,height=”+nHeight+”,toolbar=no,directories=no,status=no,scrollbars=”+ ScrollYesNo +”,resizable=”+ ResizeYesNo +”,menubar=no”);
 newWin.focus();
}

function OpenWindowYes(OpenFile,winname,nWidth,nHeight) {
 var newWin
 newWin = window.open(OpenFile,winname,”width=”+nWidth+”,height=”+nHeight+”,toolbar=no,directories=no,status=no,scrollbars=yes,resizable=yes,menubar=no”);
 newWin.focus();
}

function OpenWindowNo(OpenFile,winname,nWidth,nHeight) {
 var newWin
 newWin = window.open(OpenFile,winname,”width=”+nWidth+”,height=”+nHeight+”,toolbar=no,directories=no,status=no,scrollbars=no,resizable=no,menubar=no”);
 newWin.focus();
}

function OpenWindowPosition(OpenFile,name,nWidth,nHeight,ScrollYesNo,ResizeYesNo,lleft,ttop) {
 window.open(OpenFile,name,”width=”+nWidth+”,height=”+nHeight+”,left=”+lleft+”,top=”+ttop+”,toolbar=no,directories=no,status=no,scrollbars=”+ ScrollYesNo +”,resizable=”+ ResizeYesNo +”,menubar=no”);
}
 
 
function js_replace(str, oldStr, newStr) {
 for (var i=0;i<str.length;i++) {
  if(str.substring(i, i+oldStr.length) == oldStr) {
   str = str.substring(0, i) + newStr + str.substring(i+oldStr.length, str.length);
  }
 }
 
 return str;
}

function getAbsoluteLeft(objectId) {
 // Get an object left position from the upper left viewport corner
 // Tested with relative and nested objects
 o = document.getElementById(objectId);
 oLeft = o.offsetLeft            // Get left position from the parent object
 while(o.offsetParent!=null) {   // Parse the parent hierarchy up to the document element
  oParent = o.offsetParent;    // Get parent object reference
  oLeft += oParent.offsetLeft; // Add parent left position
  o = oParent;
 }
 // Return left postion
 return oLeft
}

function getAbsoluteTop(objectId) {
 // Get an object top position from the upper left viewport corner
 // Tested with relative and nested objects
 o = document.getElementById(objectId);
 oTop = o.offsetTop;            // Get top position from the parent object
 while(o.offsetParent!=null) { // Parse the parent hierarchy up to the document element
  oParent = o.offsetParent;  // Get parent object reference
  oTop += oParent.offsetTop; // Add parent top position
  o = oParent;
 }
 // Return top position
 return oTop;
}
function getFlashObjectHeight(objectId) {
 o = document.getElementById(objectId);
 oHeight = o.height;
 return oHeight;
}
function getFlashObjectWidth(objectId) {
 o = document.getElementById(objectId);
 oWidth = o.width;
 return oWidth;
}

Forfiles

Forfiles

[승욱] [오후 3:58] 일 받아온거하다가
[승욱] [오후 3:58] 지정된 일자만큼 파일을 보관하고 삭제한는 기능을
[승욱] [오후 3:58] 넣으라고 해서
[승욱] [오후 3:58] 찾아보다가
[승욱] [오후 3:58] 아래의 예문은 Log 디렉토리에 15일이 경과한 .log 확장자의 파일을 모두 삭제하는 명령어입니다.
   forfiles /p D:\Mail\SMTP25\Log /M *.log /D -15 /C “CMD /C del @file”
[승욱] [오후 3:58] 좋은 커맨드를 손에 넣었다
[승욱] [오후 3:58] 혼자보기 아까와서
[승욱] [오후 3:59] 저렇게 말고도, 어느날짜 이전
[승욱] [오후 3:59] 어느날짜 이후
[승욱] [오후 3:59] 로도 지정 가능하다고 함
[승욱] [오후 4:00] Forfiles 명령어 찾아보셈

SQLDeveloper DB Import/Export 방법

SQLDeveloper DB Import/Export 방법

Export는 데이터 추출이고 Import는 데이터 대입/삽입이다.

Export는 생략하고 Import 에 대해서만 설명하겠다. 어차피 쓰는 메뉴가 비슷하다.

1. 보기 – DBA 를 선택해서 DBA 탭을 활성화 시킨다.

2. 좌측 하단에 생긴 DBA 탭에서 <데이터 펌프> – <임포트 작업> 에서 우클릭하여 <데이터 펌프 임포트 마법사> 를 클릭한다.

3. 원하는 계정을 선택한다.

 

4. 임포트 마법사가 열리면 작업 이름을 적당히 써넣고, 임포트 유형을 선택한다. 여기서 디렉토리가 기본값 DATA_PUMP_DIR 이라고 되어 있는데, 이 안에 Import하고 싶은 덤프 파일(dmp 파일)을 넣으면 된다. 어디인지 감이 잡히지 않을 것이다.

5. 아래와 같이 친절한 에러가 나기 때문에, alert에 뜬 경로 안에 원하는 덤프 파일(dmp파일)을 넣어서 다시 시도하면 된다. 이후 과정은 특별히 어려울 게 없다.

덤프 파일(dmp 파일)을 넣어놓은 폴더에 IMPORT.log가 쌓이게 되는데, 메모장으로 열린다. 에러 내용을 읽어보고 적절히 조치하면 된다. 참고로 필자는 처음에 User가 없다는 에러가 떴고, User를 만들어 재시도 했을 때 TableSpace가 없다는 에러가 떴다. 참고삼아 관련 내용을 기록해둔다.

1. Oracle User 생성

create user xf identified by xf;

grant connect, resource, create view to xf;

2. Oracle TableSpace 생성

(1) <보기> – <DBA> 클릭하여 DBA 탭 표시

(2) 계정에 속한 트리에서 <저장 영역> – <테이블 스페이스> 우클릭하여 – <새로 만들기> 클릭

이후 <이름> 인풋박스에 원하는 테이블스페이스명을 기입해서 만들면 된다.

ORACLE 서비스 정보 기억나지 않을 때 (NetManager)

ORACLE 서비스 정보 기억나지 않을 때 (NetManager)

ORACLE 서비스 이름, 호스트 이름, 포트번호 등 서비스 정보를 분실했거나 기억나지 않을 때, NetManager를 실행해보면 정보가 나온다. 위 정보를 이용해 SQLDeveloper 에 접속하면 된다.

오라클 sys, system 패스워드 분실

오라클 sys, system 패스워드 분실

로컬에 오라클이 설치 된 경우에만 가능하다.

 

cmd 실행

sqlplus “/as sysdba”

alter user sysadm identified by 새패스워드;

[Javascript] 자바스크립트 인풋박스 숫자 체크 / 숫자 정규식

[Javascript] 자바스크립트 인풋박스 숫자 체크 / 숫자 정규식

문자열에서 숫자만 가져오기 (숫자를 제외한 모든 문자 빈값으로 치환)

function getNumbersOnly(_str) {

    if (_str == null || _str == “”) {

        return “”;

    }

    _str = _str + “”;

    return _str.replace(/[^\d]+/g, “”);

}

문자열이 숫자로만 구성되어 있는지 체크

function checkNumbersOnly(_str) {

    if (_str == null || _str == “”) {

        return false;

    }

    _str = _str + “”;

    var regEx = /^[0-9]*$/;

    return regEx.test(_str);

}

숫자만 입력 가능하도록 인풋박스 keyCode 체크

아래처럼 코딩하면 숫자가 아닌 값은 입력 자체가 막히게 된다.

<html>
<head>
<script>
    function onkeypress_InputObj(_e) {
        // 숫자 허용
        if (_e != null && _e.keyCode >= 48 && _e.keyCode <= 57) {
            return true;
        }

       

        return false;
    }
</script>
</head>
<body>
    <input type=”text” id=”input_obj” onkeypress=”return onkeypress_InputObj(event)” value=”” />
</body>
</html>

window.open 으로 dialog 컴포넌트 개선처리

window.open 으로 dialog 컴포넌트 개선처리

대박이다. 쇼모달 제거하면서 알게된 게 엄청 많다.

일단 <쇼모달>이란 자바스크립트에서 새 창을 띄우는 명령어이다. 쇼모달의 특징은 새 창이 뜨면서 뒤쪽의 창이 클릭 불가 상태가 된다는 것이다. 이것을 제거해야 하는 이유는 현재 IE만이 쇼모달을 지원하며, 크롬과 파이어폭스가 오래전 쇼모달 지원을 중단했기 때문이다. 그러므로 크로스 브라우징(여러 브라우저에서 사용)을 지원하려면 쇼모달을 제거해야 한다.

쇼모달을 대체할, 새 창을 띄우는 명령어가 하나 더 있다. 윈도우오픈(window.open)이다. 윈도우오픈은 새 창이 떠도 뒤쪽을 클릭 가능하다는 특징이 있다. 그러니까 쇼모달을 윈도우오픈으로 바꾸면서, 사이드 이펙트가 생기게 된다.

크게 2가지다.

(1) 쇼모달은 창이 닫힐 때까지 다음 로직 진행 중지됨. 윈도우오픈으로 바꾸면 동기식 -> 비동기식이 되어 콜백 구현 필요.

(2) 자식창을 열어도 부모창 클릭이 가능해짐

치명적인 문제는 1번이었는데 툴을 만들어서 어느 정도 해결을 했고, 2번이 문제가 되었다. 2번은 치명적이진 않지만 까다로웠다. 여기서는 2번에 대해서만 기록하겠다. 자식창을 열어도 부모창 클릭이 가능해지면서, 자식창을 n개 띄우는 문제가 발생하였다. 이것을 1개로 제한하기 위한 코드가 필요했다.

기존에 공통 컴포넌트로 다이얼로그 js가 있는데, 아래와 같은 방식으로 구현되어 있었다.

var 전역변수 = null;

(중략)

전역변수 = openDialog(“전역변수명”, …);

보다시피 리턴받는 변수와 첫 번째 아규먼트의 이름을 동일하게 만들었다. 기억해두자.

그리고 openDialog 함수는 아래와 같은 방식으로 구현되어 있었다.

window.open(주소, 전역변수명, 크기및상태바옵션);

여기서 2번째 아규먼트에 전역변수명을 넘기는 이유는, 2번째 아규먼트가 target값이기 때문이다. 윈도우오픈은 target값으로 중복 없는 윈도우를 지원한다. target값이 같으면 같은 윈도우를 공유한다. 한 마디로 윈도우의 고유이름이라고 보면 된다. 정리하면,

window.open(주소, “aaaWin”, “”); 식으로 명령을 쓰면 n번을 띄워도 창 1개만 뜬다. 즉 새로고침만 되고 창이 늘어나지 않는다.

window.open(주소, “”, “”); 식으로 2번째 아규먼트를 비우면 n번이 뜨게 된다.

그렇다면 자식창에서 부모창을 어떻게 제어하는가? 정확히 표현하면, 부모창이 갖고 있는 자식창 객체를, 자식창 쪽에서 어떻게 얻을 수 있는가? 자식창의 window.open 함수 안쪽(윈도우가 로딩될 때 실행되는 부분)에 아래와 같이 코딩하면 된다.

// window.name == “aaaWin”

var 부모가가진자식창 = window.opener.[window.name];

window.opener 혹은 window[“opener”]라고 쓰면 부모창을 가져올 수 있다. 둘은 같은 말이다.

그리고 window.opener.aaaWin 혹은 window[“opener”].[“aaaWin”]이라고 쓰면 부모창에 선언된 aaaWin이라는 전역변수를 가져온다. 이게 <부모창이 갖고 있는 자식창 객체를, 자식창이 얻은 상황>이다.

문제는 이렇게 이름을 정해두면, 항상 1개의 윈도우를 유지한다는 것이다. 이게 왜 문제가 되는지 살펴보자.

예를 들어 A라는 윈도우가 있다. 이 A 윈도우는 사용자 요구로 인해 n번을 띄워야 하는 창이다. 그런데 A라는 창 안에 어떤 버튼을 눌러서 B라는 자식창을 띄워야 한다고 해보자. 이 B라는 자식창은 n번이 떠서는 안되고 딱 1번만 떠야 하는 상황이다.

그런데 A 윈도우를 3개 띄우고, 각각의 창에서 B라는 창을 띄운다고 해보자. A 윈도우는 3개인데, B라는 창은 1개의 윈도우로 공유하게 된다. 이렇게 되면 곤란하다. B 윈도우가 여러개 떠서는 안되는 것은 사실이지만, 그것은 A 윈도우가 1개일 때 얘기다. A가 5개가 떴다면, B는 5개로 유지가 되어야 하고, 5개씩 A와 B 서로 1:1로 매핑되어야 한다.

위 문제를 해결하기 위해 유니크 키를 만들어서 다이얼로그 js 에 심었다.

// 유니크키 대입

var 유니크키 = makeUniqueKey();

// 유니크키 생성 함수

function makeUniqueKey() {
    var rand = (Math.floor(Math.random() * 10000) + 1) + “”;
 
    var todayObj = new Date();
    var todayYear = todayObj.getFullYear();
    var todayMonth = (todayObj.getMonth() + 1);
    var todayDay = todayObj.getDate();
 
    var result = todayYear + make2digit(todayMonth) + make2digit(todayDay) + “_” + rand;
    return result;
 
    // private function
    function make2digit(_num) {
        if (_num == null) {
            return “00”;
        } else {
            _num = _num + “”;
        }
  
        if (_num.length == 1) {
            return “0” + _num;
        } else if (_num.length == 2) {
            return “” + _num;  
        } else {
            alert(_num);
            return “” + _num.substring(0, 2);
        }
    }
}

보다시피 날짜와 시간 그리고 랜덤숫자를 유니크키라는 변수에 담는 내용이다. 부모창은 다이얼로그 js(위 내용이 담긴 js)를 임포트하고 있다. 이렇게 되면 윈도우마다 유니크 키를 갖는 효과가 있다.

그리고 윈도우를 띄울때 아래와 같이 띄우는 것이다.

window.open(주소, 윈도우이름 + 랜덤키, “”);

이렇게 되면 윈도우 A를 n개 띄웠을 때, 같은 윈도우 A지만 서로 다른 유니크 키를 가질 것이다. 이들이 초기화할 때 유니크키가 지정되므로, 각 윈도우에서 윈도우 B를 띄우면 각 윈도우마다의 유니크기가 붙기에 앞서 목표했던 1:1 매핑이 된다. 즉 윈도우 A가 5개라면 윈도우  B도 5개가 한계다.

그리고 자식창에서 부모창 가져올 수 있게 함수를 만들었다.

var g_DialogList = [];

function getDialogObj(_windowName) {
    if (g_DialogList == null || g_DialogList.length == 0) {
        alert(“g_DialogList == null || g_DialogList.length == 0”);
        return null;
    }

    _windowName = _windowName + “”;
 
    var winObj = null;
    var len = g_DialogList.length;
    for (var i=0; i<len; i++) {
        winObj = g_DialogList[i].m_objDlgCtrl;
        if (winObj == null) {
            continue;
        }
  
        if ((winObj.name + “”) == _windowName) {
            return g_DialogList[i];
        }
    }

    return null;
}

이제 자식창에서는 window.onload 부분에서 아래와 같이 쓰면 된다.

var 부모가가진자식창 = window[“opener”].getDialogObj(window.name);

추가로, 이제부터가 정말 중요한데, 이렇게 끝나는 줄 알았는데 한 가지 이슈가 더 있었다.

간헐적으로 <액세스가 거부되었습니다> 라는 에러가 뜨는 것이다.

부모창과 자식창의 도메인이 상호 다를 때 발생하는 이슈이다. 다시 말해서 부모창은 naver.com/어쩌고저쩌고 인데 자식창은 daum.net/어쩌고저쩌고 일때 발생하는 에러다. 이럴 때 도메인을 맞춰줘야 하는데, 다시 말해 document.domain 과 window.opener.document.domain 를 맞춰줘야 한다. 실험결과 부모창에서 자식창의 도메인 값을 바꿔쳐넣을 수는 있지만, 자식창에서 부모창의 도메인을 바꿔치려고 하면 또 다른 에러가 난다(사용 권한이 없습니다).

그래서 부모창이 창을 여는 라인(윈도우오픈) 바로 밑에 도메인 맞춰주는 로직을 넣었다.

var 자식창 = window.open(어쩌고저쩌고);

(중략)

// 도메인 변경코드 : 액세스가 거부되었습니다 처리
try {
    var dom = document.domain + “”;
    if (dom.indexOf(“http://”) < 0) {
        dom = “http://*.” + dom;
    }

    document.domain = dom;
    자식창.document.domain = dom;
} catch (e) {}

이렇게 되어서 문제가 해결된줄 알았다. 그런데 간헐적으로 계속 문제가 생긴 것이, 도메인 변경코드가 자식창이 떠서 부모창에 액세스하기 전에 처리되어야 할텐데, 자식창의 소스가 작아서 빨리 뜰 경우에 랜덤하게 문제가 생겼다. 도메인을 먼저 변경해야 하는 것이다.

그래서 모든 과정을 다 롤백해야 하나 심각하게 고민하고 있었는데, 애초 문제 없이 잘 돌아가고 있었던 코드가 떠올랐다.

var 부모가가진자식창 = window.opener.[window.name];

이 코드는 윈도우 객체 안의 opener 객체 안의 window.name에 해당하는 객체를 가져오는 코드였다.

여기서 깨달은 것은, 결국 객체 접근은 오류가 나지 않는다. 함수를 사용했을 때 “액세스가 거부되었습니다”, “사용 권한이 없습니다”가 나는 것 같았다. 번개 같이 스친 생각에 배열을 임시로 만들어 돌려봤더니 접근이 된다!

그래서, 자식창에서 부모가가진자식창을 갖고 오기 위한 함수 – window.opener.getDialogObj(window.name); – 를 다음과 같이 대체했다.

var 부모가가진자식창 = window.opener.g_DialogMap[window.name];

자식쪽에 이렇게 깔끔하게 해결했다. 원래 eval을 사용하려 했으나 eval은 비권장이기에 대괄호를 사용했다.

부모쪽에는 var g_DialogMap = {}; 이렇게 객체 하나만 만들어주면 된다.

이렇게 하면 for문 돌면서 객체 찾아오는 짓도 안해도 괜찮다.

듀얼모니터 고려한 window.open 띄우기

듀얼모니터 고려한 window.open 띄우기

브라우저에서 새 창을 띄울 때 자바스크립트 window.open 함수를 쓰는데, left, top, width, height 를 지정할 수 있다. 이 때 듀얼모니터 사용자의 경우 개발자 의도와 다르게 나올 수 있어 위치를 조정해줘야 한다.

네이버 검색이랑 구글링을 열심히 해봤는데 막상 소스 떠다가 적용해보면 결과가 형편없어서 그냥 새로 만들었다.

현재 띄워져 있는 창을 부모창이라고 해보자. 부모창이 어떤 모니터에 떠있나를 따지기는 비효율적이고, 그냥 자식창을 부모창 가운데에 띄워주면 된다.

자식창Left값 : 부모창Left값 + 부모창Width반값 – 자식창Width반값;

자식창Top값 : 부모창Top값 + 부모창Height반값 – 자식창Height반값;

문제는 윈도우 운영체제에서 듀얼 모니터를 어떻게 설정하냐에 따라 window.open 이 위치를 잘못 잡는다.

해상도 설정에 가면 1번 모니터, 2번 모니터가 있다. 편의상 좌측 모니터를 1번, 우측 모니터를 2번이라고 하자.

좌측 모니터를 주모니터로 하면, 좌측 모니터의 width값의 시작은 0px부터, 우측 모니터의 width값의 시작은 1000~2000px 대에서 시작할 것이다.

그런데 우측 모니터를 주모니터로 하면, 우측 모니터의 width값의 시작이 0px이기 때문에, 좌측 모니터 width값이 마이너스대가 된다.

그런데 window.open은 left나 top값이 마이너스일 경우, 그냥 모니터 가운데에 창을 띄워버린다.

즉, 좌측 모니터가 주모니터일 때는 좌우측 모두 width 값이 양수이므로 부모창의 가운데 위치를 잘 찾지만,

우측 모니터가 주모니터일 때는 우측의 경우 정상적으로 부모창의 가운데에 띄우고, 좌측의 경우 그냥 모니터 가운데 띄우게 된다.

한 마디로 대칭성이 깨지는 것이다.

대칭성을 확보하기 위해서는 window를 일단 띄우고 move 시키는 방법, 모니터가 좌측인지 중앙인지 우측인지 판별해서 부모창 가운데 위치를 찾지 말고 그냥 모니터 가운데로 띄워주는 방법, 이렇게 두 가지를 생각해보았는데, 비효율적이므로 구현하지는 않는다.

<예제>

var nWidth = “500”;
var nHeight = “300”;
  
// 듀얼 모니터 고려한 윈도우 띄우기
var curX = window.screenLeft;
var curY = window.screenTop;
var curWidth = document.body.clientWidth;
var curHeight = document.body.clientHeight;
  
var nLeft = curX + (curWidth / 2) – (nWidth / 2);
var nTop = curY + (curHeight / 2) – (nHeight / 2);

var strOption = “”;
strOption += “left=” + nLeft + “px,”;
strOption += “top=” + nTop + “px,”;
strOption += “width=” + nWidth + “px,”;
strOption += “height=” + nHeight + “px,”;
strOption += “toolbar=no,menubar=no,location=no,”;
strOption += “resizable=yes,status=yes”;
  
var winObj = window.open(this.m_strDlgUrl, _strName, strOption);
if (winObj == null) {
    alert(“팝업 차단을 해제해주세요.”);
    return false;
}

<설명>

innerHTML, innerText 차이

innerHTML, innerText 차이

1. SET

– 태그가 없는 값 세팅의 경우

innerHTML과 innerText 차이 없다.

– 태그가 들어있는 값 세팅의 경우

innerHTML는 html 이 들어가고, innerText 는 텍스트 그대로 들어간다.

2. GET

– 태그가 없는 값 세팅의 경우

innerHTML는 값 그대로 얻음, innerText 는 공백이 제거

충격적인 예제 (1.htm)

—————–

<html>
<head>
<script>
function getHtml() {
    alert(“[” + rr.innerHTML + “]”);
}
function setHtml() {
    rr.innerHTML = ”  <br>흑곰흑곰  “;
}

function getText() {
    alert(“[” + rr.innerText + “]”);
}
function setText() {
    rr.innerText = ”  <br>흑곰흑곰  “;
}
</script>
</head>
<body>
<div id=”rr” name=”rr”></div>

<input type=”button” value=”setHtml” onclick=”setHtml()”>
<br>
<input type=”button” value=”getHtml” onclick=”getHtml()”>
<br>
<br>
<input type=”button” value=”setText” onclick=”setText()”>
<br>
<input type=”button” value=”getText” onclick=”getText()”>
<br>

</body>
</html>

javascript 필드 동적 생성/제거 (innerHTML, appendChild)

javascript 필드 동적 생성/제거 (innerHTML, appendChild)

자바스크립트에서 동적으로 필드를 생성하기 위해 innerHTML을 사용해 div 필드를 붙여넣어 보았다.

var newDivHtml = “<div id=’aaa’></div>”;

document.body.innerHTML = document.body.innerHTML + “” + newDivHtml;

그러나 이 경우, 화면에는 표시가 되지만(동적 생성은 되지만), 동적 삭제가 불가능하다.

다시 document.body.innerHTML 를 출력해보면 문자열 상에서 id가 aaa인 div를 찾을 수 없기 때문이다.

구글링 해본 결과, innerHTML로 추가한 결과 문자열을 얻으려면 document.documentElement.innerHTML 을 사용하면 된다고 했지만, 테스트 결과 찾을 수 없었다.

결국 유효한 코드는 다음과 같다.

———- <javascript div 동적 생성> ———-

var newDivHtml = “<span>추가할 내용</span>”;

var div = parent.document.createElement(“div”);
div.id = “aaa”;
div.innerHTML = newDivHtml;

// body에 심기

parent.document.getElementsByTagName(“body”)[0].appendChild(div);

// 특정 필드에 심기

// parent.document.getElementById(“targetDiv”).appendChild(div);

—————————————-

———- <javascript div 동적 제거> ———-

function remove(id) {
    var elem = document.getElementById(id);
    return elem.parentNode.removeChild(elem);
}

remove(“aaa”);

—————————————-

이러한 innerHTML 을 사용할 때 문제가 하나 있었는데, 바로 for 문 또는 while 문 안에서 사용하는 경우였다.

for문 안에서 innerHTML 이나 appendChild 를 사용하면

중간 과정이 html 상에 그려지지 않는 문제가 있었다.

루프를 한 번 돌때마다 alert을 띄우면 중간 과정이 그려졌는데 그러자면 innerHTML을 사용하는 의미가 없어진다.

구글링을 해보았지만 외국 코더 분들도 딱히 답이 없어보였다.

그 중에 제일 나은 답안이 for문 혹은 while 문을 setInterval 로 바꾸는 방안이었다.

예를 들어 while 문이 아래와 같이 있다면

————— <AS-IS> —————

var i = 초기값;

while(조건) {

    // 내용…

}

// 루프 빠져나와서 수행할 내용

—————————————-

————— <TO-BE> —————

var i = 초기값;

var loop = window.setInterval(function(){

    if (!(조건)) {

        window.clearInterval(loop);

        // 루프 빠져나와서 수행할 내용

    }

    // 내용…

}, 100);

—————————————-