2019년 2월 13일 수요일

node.js - 하드웨어 정보 추출하기

node.js 런타임에서 맥 어드레스, CPU ID등의 하드웨어 정보를 추출하는 코드.


const wmic = require('ms-wmic');

function getCleanWmicResult(dirtyString) {
    if (dirtyString.indexOf('\r') > -1) {
        dirtyString = dirtyString.substring(dirtyString.lastIndexOf('\r') + 1);
    }
    return dirtyString;
}

// get hardware info, using wmic.exe
function getHWInfo() {
    return new Promise(function (resolve, reject) {
        wmic.execute('nic where netenabled=true get MACAddress', function (err, stdOut) {
            if (err) reject(new Error("error case #1"));
            let macAddress = getCleanWmicResult(stdOut).split(':').join('-');
            
            wmic.execute('cpu get processorId', function (err, stdOut) {
                if (err) reject(new Error("error case #2"));
                let cpuID = getCleanWmicResult(stdOut);
                
                resolve({ 'macAddress': macAddress, 'cpuID': cpuID });
            });
        });
    });
}

getHWInfo().then(function (data) {
    // data.macAddress, data.cpuID, ...
}).catch(function (err) {
    console.error(err);
});


ms-wmic 모듈을 이용하였다. ms-wmic 모듈이 윈도우의 wmic.exe를 참조하는 방식으로 동작하기 때문에 wmic.exe 위치가 PATH 환경 변수에 포함되어 있어야 한다. 아니면 wmic.exe를 노드 실행 파일과 같은 위치에 두어도 된다.

getHWInfo()가 하드웨어 정보를 추출하는 코드고, getCleanWmicResult()란 함수를 만든 이유는 ms-wmic가 반환해주는 문자열에 캐리지 리턴(carriage return)이 포함되어 있어서다. (캐리지 리턴 문자 때문에 다른 문자열과 연산했을 때 값이 깨진다.)

* 어쨌든 WMIC(Windows Management Instrumentation Command-line)를 이용하면 윈도우 환경을 한정해서 메인보드 ID, HDD ID 등 하드웨어 인증에 이용될 수 있는 값들을 구할 수 있다는 것.

2019년 1월 25일 금요일

jQuery.ajax 파라미터 설명.

http://api.jquery.com/jquery.ajax/
write less, do more.

jQuery.ajax()는 비동기 HTTP 요청을 수행할 때 쓴다.

jQuery.ajax(url [, settings])의 실로 간단한 함수 표기지만 settings에 들어갈 수 있는 내용이 꽤나 복잡하다. 표기대로 모든 세팅 항목은 선택사항이고, 디폴트 설정은 $.ajaxSetup()을 이용해 지정할 수 있다.

1. accepts: 응답받고 싶은 형식을 서버에게 알려주는데 쓰인다. text/* 라면 텍스트 형식이면 어떤 것이든 괜찮다는 의미로 서버에게 전달되는 것이다.

2. async: 기본값은 true이고, false로 바꾸는 경우 동기 요청이 된다. 동기 요청인 경우 서버로부터 응답이 오기까지 브라우저가 락된다는 의미가 된다. dataType이 jsonp이거나 크로스-도메인 요청인 경우 false로 세팅할 수 없다.

3. beforeSend: ajax 요청 전에 필요한 동작을 지정할 수 있다.

4. cache: 캐쉬 옵션. false인 경우 요청 페이지가 브라우저에 의해 캐시되지 않도록 한다.

5. complete: 요청이 완전히 끝난 다음에 호출된다. (success, error 콜백 이후에.)

6. contents: jQuery가 응답을 어떻게 파싱할 것인지에 대한 방법을 결정해주는 설정. (잘 모르겠음.)

7. contentType: 서버에게 전달할 때 알려주는 데이터 형식. 기본값은 'application/x-www-form-urlencoded; charset=UTF-8'.  false 값을 지정하는 경우엔 jQuery에게 컨텐트 유형 헤더를 설정하지 말라는 의미가 된다.

9. context: ajax 관련 콜백의 컨텍스트를 지정한다.

10. converters: 특정 데이터 형식에 대한 converter를 지정해줄 수 있다.

11. crossDomain: 크로스-도메인 요청 (such as JSONP)을 같은 도메인에서 강제로 호출하고 싶은 경우 true 값을 준다.

* JSONP: JSON with Padding. 다른 도메인으로부터 데이터를 가져오고 싶은 경우 사용하게 되는 편법. cross-domain 이슈를 회피하기 위해 json 형식의 데이터를 script 요소로 감싸 실행시키는 것처럼 하여 데이터를 가져오는 기법.

12. data: 서버로 전달할 데이터. 데이터가 query string 형태로 변환되는 것을 원하지 않으면 processData를 false로 설정해주어야 한다.

13. dataFilter: XMLHttpRequest의 raw 응답 데이터를 핸들링하기 위한 함수를 지정할 수 있다.

14. dataType: 서버의 응답으로부터 예상되는 데이터 형식. 기본적으론 jQuery가 추측해준다.

15. error: 요청이 실패했을 때의 콜백.

16. global: 잘 모르겠다.

17. headers: XMLHttpRequest 전송을 사용하여 요청과 함께 전송할 추가 key/value 쌍.

18. ifModified: 잘 모르겠다.

19. isLocal: 현재 환경을 '로컬'로 인식하도록 할 수 있다.

20. jsonp: JSONP 요청에서 쿼리 스트링 'callback='의 'callback' 대신 사용할 이름을 오버라이딩 한다.

21. jsonpCallback: JSONP 요청의 콜백 함수 이름을 지정한다.

22. method: GET, POST, PUT, ..

23. mimeType: XMLHttpRequest mime type을 재정의한다.

* MIME: Multipurpose Internet Mail Extentions. 전송된 문서의 형식을 알려주기 위한 메커니즘. type/subtype의 구조로 '/'로 구분해 type과 subtype으로 나뉜다. 공백은 허용되지 않고, 소문자로 쓰는것이 관례. type은 개별 타입과 멀티파트 타입으로 구분된다.
개별 타입은 text, image, audio, video, 마지막으로 이진 데이터 형식의 application으로 구분된다. 특정 subtype이 없는 텍스트엔 text/plain이 쓰이고, 특정 subtype이 없는 이진 데이터엔 application/octet-stream이 쓰인다.
멀티파트 타입은 multipart/form-data, multipart/byteranges로 구분되는데 multipart/form-data는 브라우저에서 서버로 HTML Form의 내용을 전송할 때 쓰인다.

24. password: HTTP 엑세스 인증 요청에 대한 응답으로 XMLHttpRequest와 함께 사용할 암호.

25. processdata: object 형식의 데이터가 전달될 때 쿼리 스트링으로 변환할 것인지 여부를 지정한다.

26. scriptCharset: 크로스-도메인 요청같은 것을 할 때 로컬 페이지의 charset과 원격 스크립트의 charset이 같지 않을 때 쓴다.

27. statusCode: 각종 응답 코드에 대한 동작을 지정할 수 있다.

28. success: 요청이 성공했을 때의 콜백.

29. timeout: 요청에 대한 타임 아웃을 지정할 수 있다. '0'인 경우 무한정 기다린다.

30. traditional: traditional style의 param 직렬화를 사용하고 싶은 경우에 true를 지정하면 된다.

31. type: alias for 'method'

32. url: URL

33. username: HTTP 엑세스 인증 요청에 대한 응답으로 XMLHttpRequest와 함께 사용할 사용자 이름.

34. xhr: XMLHttpRequest 객체를 만들기 위한 콜백.

35. xhrFields: 네이티브 XMLHttpRequest 객체를 설정할 field-name/field-value 쌍.


2019년 1월 2일 수요일

[Netty] IdleStateHandler - Idle 상태를 감지하는 핸들러.

https://netty.io/4.0/api/io/netty/handler/timeout/IdleStateHandler.html

IdleStateHandler는 특정 시간 동안 read, write, 또는 read/write 동작이 없을 때 이를 체크해 사용자에게 알려주는 역할을 수행한다. IdleStateHandler는 Inbound와 Outbound를 모두 처리하는 ChannelDuplexHandler를 상속받아 구현되어 있다.

보통 인스턴스를 만드는데 사용하는 생성자는 아래와 같다.
IdleStateHandler(int readerIdleTimeSeconds, int writerIdleTimeSeconds, int allIdletimeSeconds)

앞서 적혀있듯이 IdleStateHandler는 read에 대한 idle 감지, write에 대한 감지, read/write에 대한 idle 감지의 3가지 타입을 제공해주고 있다. 내부적으로는 one-shot 타이머 쓰레드가 반복적으로 송수신 메시지가 있는지 없는지를 확인해주는 방식이므로 불필요한 자원을 낭비하지 않으려면 필요없는 타입의 timeout 파라미터에 '0' 값을 전달해 해당 기능을 끄는게 좋다.

마지막으로 사용자는 IdleStateHandler가 통지하는 이벤트를 받아 처리하기 위해 ChannelDuplexHandler를 상속받아 userEventTriggered 메서드를 구현해야 한다.

온라인 API 문서엔 다음과 같은 예제가 있다.


// 60초 동안 수신하는 메시지가 없으면 MyHandler에 의해 클라이언트와의 연결을 끊는다.
// 30초 동안 송신하는 메시지가 없으면 MyHandler가 핑 메시지를 클라이언트로 전송한다.
public class MyChannelInitializer extends ChannelInitializer<Channel> {
    @Override
    public void initChannel(Channel channel) {
        channel.pipeline().addLast("idleStateHandler", new IdleStateHandler(60, 30, 0));
        channel.pipeline().addLast("myHandler", new MyHandler());
    }
}

public class MyHandler extends ChannelDuplexHandler {
    // 사용자는 userEventTriggered에서 이벤트의 상태를 읽어 필요한 동작을 지정해주면 된다.
    @Override
    public void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception {
        if (evt instanceof IdleStateEvent) {
            IdleStateEvent e = (IdleStateEvent) evt;
            if (e.state() == IdleState.READER_IDLE) {
                ctx.close();
            } else if (e.state() == IdleState.WRITER_IDLE) {
                ctx.writeAndFlush(new PingMessage());
            }
        }
    }
}

ServerBootstrap bootstrap = ...;
...
bootstrap.childHandler(new MyChannelInitializer());
...

2018년 12월 25일 화요일

Elastic Stack 6.5 업데이트.

사용할 계획은 없지만 관심은 항상 있는 Elastic Stack의 6.5 업데이트 소식.
마이너 버전 업이지만 마이너한 것은 없는 릴리즈라는 개발사의 얘기처럼 방대한 양의 기능 업데이트가 있었다.

https://www.elastic.co/kr/blog/elastic-stack-6-5-0-released


Cross-Cluster Replication (Beta | Platinum)

한 클러스터의 인덱스를 다른 클러스터로 이중화(replicate) 해준다. 기존의 다른 클러스터 이중화 기법들보다 오버헤드가 적고, 추가적인 자원(임시 저장장치 등)이 필요하지 않으며 시스템 복잡성도 낮다. Follower가 Leader를 실시간 룩업, 인덱스 변화를 관찰하여 변경점을 복제한다.

이 기능을 활용함으로서 장애 복구(Disaster Recovery), 글로벌 서비스의 경우 지역별로 복제본을 두어 시스템 응답성을 높일 수 있다. (Data Locality) 그리고 여러개로 나누어져 운용되는 클러스터의 데이터를 한군데로 모아 통합 쿼리를 수행하거나 리포트를 만들 수도 있다. (Central Reporting Center)


_source Only Snapshot (데이터 복원 기능)

전체 풀 스냅샷 대비 50%정도의 저장 공간을 차지한다. 소스만 저장하고 복원하는 방식이기 때문에 복원(restore)하는데 재색인을 필요로 한다. 긴 복원 시간과 적은 저장 공간의 trade off.


Canvas !! (Beta | Basic)

Kibana의 신규 기능으로 Canvas는 기존 대비 향상된, 마치 파워포인트를 이용해 페이지를 만드는 것과 유사한 경험을 가져다 준다. 데이터의 라이브 쇼케이스 기능으로 나만의 방식으로 데이터에 대한 스토리텔링을 할 수 있다. Elasticsearch SQL을 지원하고 보고서 파일 생성이 가능.


Rollups in Kibana (Beta | Basic)

자동으로 데이터를 집계가 필요한 부분만 roll up 해서 관리한다. 통계성 데이터만 저장하므로 데이터의 저장 공간이 절약되고 적은 양의 데이터를 대상으로 하기 때문에 집계 쿼리가 빨라진다.

기타 6.5엔 Beats를 중앙에서 관리하는 도구인 Beats Central Management, 서버리스 아키텍쳐의 function 레벨로 사용가능한 새로운 타입의 Beat은 Functionbeat. Metric Beat와 연동하여 수많은 인프라 기반의 동작 데이터를 한 눈에 볼 수 있는 큐레이션 도구인 Infrastructure UI 등의 신규 기능이 도입되었다. Basic 라이센스로 제공되는 Machine Learning의 Data Visualizer는 사용자가 업로드하는 데이터 파일의 구조를 시각화해준다. 대략적인 데이터의 속성과 분포를 알려주는 것 뿐만 아니라 잘 아는 형식에 대해선 Grok pattern도 제공해주고 있다.

Java 11 버전을 지원하며 G1 가비지 컬렉터를 사용할 수 있게 되었으며 이번 업데이트를 통해선 6.3에서 지원하던 JDBC 드라이버 뿐만 아니라 ODBC 드라이버도 지원한다. Elastic Stack은 데이터를 다루는 솔루션이 제공해야할 기능의 모범사례를 누구보다 빠르고 적확하게 제공하기 때문에 데이터 관련된 일을 하는 사람이라면 Elastic Stack을 사용하지 않더라도 변경점에 대해서 관심을 가져야 할 것이란 생각이 든다.


2018년 12월 19일 수요일

시리얼 통신 (RS-232)

시리얼 통신

1. 직렬 통신은 하나 또는 두 개의 전송 라인을 사용하여 한 번에 한 비트씩 데이터를 지속적으로 송수신하는 통신 방법이다.

2. 직렬 통신에서 데이터는 하나의 신호선을 사용해 한번에 한비트씩 전송되므로, 수신측에서 데이터를 정확하게 수신하려면 송신측에서 비트를 어떤 속도로 전송하는지 알고 있어야 한다.

3. 전송 표준으로 RS-232C, RS-422A, RS-485가 있다.

4. 커넥터는 주로 D-sub 25핀, D-sub 9핀이 사용된다.
 DTR: Data Terminal Ready, 데이터 터미널 준비
 TXD: Transmit Data, 전송 데이터
 RXD: Receive Data, 수신 데이터
 RI: Ring Indicator, 착신 표시
 CTS: Clear to to Send, 송신 허가
 RTS: Request to Send, 송신 요구
 DSR: Data Set Ready, 데이터 세트 준비
 ,..... 기타

5. 설정 값.
 5.1 전송 속도: 초당 보낼 비트 수를 지정한다. (Baudrate)
 5.2 정지 비트 길이: 데이터의 끝을 나타내는 비트의 길이. 일반적으로 1, 1.5, 2비트.
 5.3 데이터 비트 길이: 일반적으로 영숫자 및 기호에는 7비트, 1바이트 이진 데이터에는 8비트.
 5.4 패리티 체크 설정: 데이터에서 오류를 찾는 기능. 짝수 패리티, 홀수 패리티, 패리티 없음 중 하나.

시리얼 통신은 Com Port를 통해 Read Write하는 단순한 구조기 때문에 Open, Close, Read, Write의 4가지 명령을 통해 작업을 제어하게 된다.

.Net Framework 환경에서의 프로그래밍

SerialPort 클래스가 정의되어 있고, 데이터 수신, 에러 수신, 핀 체인지에 대한 이벤트 설정을 할 수 있게 되어 있다. {System.IO.Ports (Assemblies: System.dll, System.IO.Ports.dll)}

설명 및 예시:
https://docs.microsoft.com/ko-kr/dotnet/api/system.io.ports.serialport?view=netframework-4.7.2


Win32 API 환경에서의 프로그래밍

파일 다루듯 다루어진다.

 1. 포트 열기: CreateFile
 2. 포트 구성: SetCommState, SetCommTimeouts
 3. 데이터 전송: WriteFile
 4. 데이터 수신: ReadFile
 5. 장치 모니터링: SetCommMask

써야되는지 알고 식겁했다.

2018년 12월 10일 월요일

[Spring] Configuration, Bean 어노테이션

org.springframework.context.annotation의 Configuration 어노테이션과 Bean 어노테이션을 이용하면 Spring Container에 새로운 Bean 객체를 제공할 수 있다.

@Configuration
public class AppConfig {
  @Bean
  public MyBean myBean() {
    // instantiate, configure and return bean ...
  }
}

초기화 과정에서 특정 값을 지정할 경우엔 다음과 같이 할 수 있을 것이다. (@Value, @Autowired, ..)

@Configuration
@PropertySource("classpath:/com/acme/app.properties")
public class AppConfig {
  @Inject Environment env;

  @Value("${bean.name}") String beanName;

  @Bean
  public MyBean myBean() {
    // return new MyBean(env.getProperty("bean.name"));
    // or
    // return new MyBean(beanName);
  }
}

여러 개의 @Configuration 간 의존 관계는 @Autowired나 클래스 상단에 @Import 구문을 이용해 지정할 수 있다.

@Configuration을 <XML>로 표현하면 다음과 같다.
<beans>
  <context:annotation-config/> <!-- enable ConfigurationClassPostProcessor -->
  <bean class="com.acme.AppConfig"/>
</beans>

참고로 @Configuration 어노테이션을 사용한 클래스에서 @Bean 어노테이션을 사용한 함수가 리턴하는 객체는 Singleton으로 관리된다.

적용된 내용은 AnnotationConfigApplicationContext로 가져올 수 있다.

ApplicationContext context = new AnnotationConfigApplicationContext();
context.register(AppConfig.class);
context.refresh();

MyBean myBean = ctx.getBean(MyBean.class);
// use myBean...

@Bean 어노테이션과 함께 initMethod, destroyMethod를 지정할 수 있는데 이는 각각 초기화/종료 메서드에 대한 것이다.
@Configuration과 @Bean은 @Profile 어노테이션과 결합하여 'production', 'development' 등 환경에 따라 다르게 동작하게끔 할 수 있다.

https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/annotation/Configuration.html

JPA 기본 골격.

// Entity
@Entity
@Data
@NoArgsConstructor
public class Member {
 @Id // javax.persistence.Id
 @GeneratedValue(strategy=GenerationType.AUTO)
 private long id;

 @Column(nullable = false, length = 30)
 private String name;

 private int age;

 public Member(String name, int age) {
  this.name = name;
  this.age = age;
 }

 @Override
 public String toString() {
  return String.format("Member[id=%d, name='%s', age=%d]", id, name, age);
 }
}

// Repository
public interface MemberRepository extends CrudRepository<Member, Long> {
 List<Member> findbyNameAndAgeLessThan(String name, int age);
 List<Member> findbyNameAndAgeLessThanOrderByAgeDesc(String name, int age);

 @Query("select t from Member t where name=:name and age < :age")
 List<Member> findByNameAndAgeLessThan(@Param("name") String name, @Param("age") int age);
}

// Booter
@EntityScan(basePackages="package.name")
@EnableJpaRepositories(basePackages="another.package.name")
@SpringBootApplication
public class TestJpaApplication {
 public static void main(String[] args) {
  SpringApplication.run(TestJpaApplication.class, args);
 }
}

참고로 JPA가 제공하는 Repository의 상속 계층은 다음과 같다.
CrudRepository만 써도 일반적인 CRUD 작업은 할 수 있다.

1. public interface Repository<T, ID extends Serializable>
2. public interface CrudRepository<T, ID extends Serializable> extends Repository<T, ID>
3. public interface PagingAndSortingRepository<T, ID extends Serializable> extends CrudRepository<T, ID>
4. public interface JpaRepository<T, ID extends Serializable> extends PagingAndSortingRepository<T, ID>

https://www.concretepage.com/spring-boot/spring-boot-crudrepository-example