* 해당 포스트에 업무상 메인 개발 환경과 보조로 사용하는 개발 도구들을 모아봤습니다.
* 현 업무 및 포지션: 서버(게임)




1. 메인 개발툴
 

1) 이클립스
> 주목적: JAVA, JavaScript, 스프링부트, AWS
> 단점

① '전자정부 표준프레임워크'로 나온 이클립스로 작업시, 프로젝트에 svn플러그인이 자동으로 활성되어 있다보니 윈도우 창에서 업데이트나 커밋을 할때 오류가 나고 이클립스쪽이 느리기도 하고 이쪽 svn플러그인은 연결 해제 해두는게 나을지도? 
② 종종 문법상 혹은 아까까지 멀쩡하던 코드에 다른 클래스 코드를 수정하고 프로젝트 클린등을 하고 나면 접점이 전혀없는 코드에서 에러 표시가 날때가 있어서 이럴때마다 이클립스가 참 성가신데 다행히 해결법이 좀 간단합니다. 해당 코드 파일 전체를 잘라내기-붙여넣기-저장하면 멀쩡합니다. 작동 다 확인 한 걸 껐다가 키면 에러표시날 경우에도 제법 써먹을만.


2) SVN
> 주목적: 팀원간 코드공유 및 이력관리
> 단점
① 신입분에게는 사용법 교육을 해드려야&해드리고 (git 안 써본 분도 더러...)
② 제발 trunk에는 구동이 되는거까지는 확인된걸로 올려주세요...
③ 팀원내 보안 이슈가 좀 있어서 접근 권한을 손 보려 했더니, authz파일로 한다는 내용이 대부분이라 저장소내 프로젝트 폴더 구조에서부터 신경써야;;;
 
 
3) 비쥬얼 스튜디오
> 주목적: .netCoreSDK 3.1(netcoreapp 3.0), 유니티, AWS 연동
> 단점
① 로컬 ASP.NET 세팅시 마이너 버전까지 맞춰야 구동이 되서 다른 세팅에 비해 까다로웠음.
 

4) 리눅스
> 주목적: 서버 머신
> 단점
① centOS건 우분투건 버전에 따라 명령어나 설치 경로가 달라지는 경우가 잦아서 매번 찾기 힘들어짐. 우분투의 경우 16인가, 18인가로 그냥 패키지 명령어만 같은 다른 OS로 보는게 속편함.
 
 
 
 

2. 보조용 GUI툴
 
1) Robo 3T [#홈페이지]
> 주목적: (noSQL)mongoDB GUI
> 단점
① 1.4.4가 마지막 버전(#블로그). (해당 프로젝트가 Studio 3T로 변경된다하는데 현재 쓰임새상 옮겨갈 필요가 없으면 굳이?)
② 검색된 결과들을 json으로 보면 중간중간 /*  1  */이 있어서 테이블 일부 입력값을 그대로 옮기는 못해서 정규식으로 중간 주석문을 지워야 함.
 
 
2) HeidiSQL [#홈페이지] & MySQL Workbench[#홈페이지]
> 주목적: (SQL)마리아 DB GUI
> 단점
① UI편의성등으로 HeidiSQL를 쓰긴 쓰는데, 간혹 덤프해둔 SQL문 처리가 안 될때가 있으면 Workbench로 쓰면 입력이 되다 보니 좋게 말하면 상호보완, 나쁘게 말하면 2개를 써야 내 퍼포먼스가 올라가는 이건 대체 무슨?
 
 
3) P3X Redis [#홈페이지]
> 주목적: 레디스 GUI
> 단점
① 기록이 10만을 초과한 랭킹 보드(sorted set)를 조회시, 페이징없이 전체 기록을 출력하는건지 느려진다.
② 프로그램 안정화 문제인지 간혹 실행자체가 안 될때가 있는데, 이럴때는 재설치를 하면 해결된다.





3. 보조용 에디터
 
1) 노트패드++
> 주목적: 
> 단점
① '리눅스: grep 함정과 임기응변'에도 언급했지만, 8.3.3 기준으로는 열린탭들 합쳐서인지 몰라도 500MB를 초과하면 열수 없습니다. 그렇다고 대용량 로그 파일은 못 쓰는가? 아닙니다. 뒤에 나오는 glogg로 해결은 가능합니다.
 

2) glogg [#홈페이지]
> 주목적: 대용량 로그파일 열기
> 단점
① 검색시 특수기호나 띄어쓰기가 있으면 검색이 잘 안 될때가 있어서 가급적 공백이 없고, 특수기호가 없는 단어로 검색해야 한다.
 




4. 기타, 네트워크
 
1) tcping [#홈페이지]
> 주목적: 포트 입력이 가능한 통신확인
> 단점
① 딱히?
 
 
2) WinSCP [#홈페이지]
> 주목적: FTP와 SSH를 하나로. 그리고 SSH포트만 허용된 서버에서 작업
> 단점
① 용량이 클수록(500MB초과)의 경우, Filezilla에 비해 느려지기 시작.
② 다운로드중에는 다른 명령어가 안 되어서 다운로드 목록에 파일이 추가한다거나 다른 디렉토리로 이동이 불가.
 
 
3) 포스트맨 [#홈페이지]
> 주목적: 로컬서버 및 개발서버 통신 테스트
> 단점
① 브라우저 버전(혹은 크롬 확장 프로그램)으로 스프링부트 로컬 서버와 통신시, CORS 문제가 발생할 여지가 있어서 가급적 설치 버전을 권장.





기타. 변경이력

일자
 변경이력
 2022-07-30  초안 작성. [#blogger] [#티스토리]
 오늘은 튜토리얼이나 샘플 코드가 아닌 실무에서 겪은 '괴담'에 가깝습니다.
요약본을 말하자면, 'grep 명령어가 파일 용량에 따라 결과가 상이하다.'입니다.
읽고도 '무슨 헛소리냐'싶어지죠?
저도 잘못 본 거였으면 좋겠지만, 이 이슈로 금요일 오전부터 오후까지 진땀 뺀터라.
 





당시 환경
 

> 우분투 16.04LTS, AWS ec2

> 톰캣 8.5
> 1.2~1.3GB정도 되는 톰캣 로그 3개.
> mongoDB v4.2.12

 테이블 일부를 초기화 시키다 그만 누적을 유지해야 하는 컬렉션까지 날려버린겁니다. 자동 백업본이나 수동 백업본이 아예 없던건 아니지만 시간상 몇 시간 공백이 있다 보니 대체 불가인 자료여서 어떻게든 복원을 시켜야 하는거였죠.
 그런데 어떤 방법으로?



구명 보트를 찾아라.

 noSQL인 몽고DB툴로 Robo 3T를 쓰고 있는데, 툴에 나오는 로그로는 그냥 제 멍청한 실수가 몇 시쯤 시행됐다만 재확인 시켜줄뿐 다른게 없습니다.
 
 그런데 잠깐!
 
 컬렉션에 데이터 입력시 JSON 형식으로 입력해야하는데, 디버깅용 로그가 활성중이라 톰캣 로그에는 온갖 입출력이 다 나와있고 로그 추적을 용이하게 하려고 DB에 입력된 값도 확인할 로그를 심은게 있어요.
 몽고DB건 다이나모DB건 테이블에 입력하는 자체 Item클래스는 map이지만 정규식과 문자열 바꾸기로 JSON형식에 맞게 가공이 가능하니 이제 할 일은 뭐다?

 확인겸 로그 파일에서 해당 자료 유형에만 존재하는 컬럼명을 검색해보니,
jsonPretty로 찍히긴 했어도 영 써먹지 못할 형태까지는 아닌게 확인됐습니다. 혹시 싶어 1 줄 짜리로 출력 되게 심어둔건 없는지 위아래로 찾아보니 다행히 있었습니다.

 입출력 값 로그는 사람이 파악하기 편하게 jsonPretty로 로그를 심는 것도 좋지만,
1줄 짜리 출력을 보조격으로 넣은게 구명 보트가 된거였습니다.



그런데... 보트가 움직이질 않는다?
 
 로그 파일이 다 있지만,
전부 합치면 3.5GB에 육박하는 문자열에서 내가 원하는걸 추려내야 합니다.

1) 특정 컬럼명이 있는 JSON을 한 줄내로 찍은줄만 추출하는 코드를 Junit등으로 작성?
-> 기각!
 아무리 단순 반복 기능이라도 용량상 유니티부터 MOS들이 띄워진 업무PC는 기다리면서 다른걸 한다 해도 과부하 가능성을 무시 못함. 
2) 학부 리눅스 수업에서 뭔가 비슷한 기능을 소개한거 같다.
-> 찾아보자!
 
 제가 찾은 방법은 'grep'입니다.
학부에서는 디스크내 파일 검색만 해봤지, 파일내에서 검색은 처음이라 은근히 헤매게 되더군요. (자료 복구가 걸린 일인것도 있지만...)
 
grep '도큐먼트_컬럼명' tomcat_A.2022-05-XX.log
 원하던 형태로 추려져서 검색이 동작한다!
 
 그런데 내가 필요한건 추출본...
검색 결과 그대로 파일에 저장 하는게 뭐였더라로 잠시 또 구글링.
 
grep '도큐먼트_컬럼명' tomcat_A.2022-05-XX.log > tomcat_crop.txt
난 이제 살았다! 자유야!
...
...
로 저기서 해결됐으면 굳이 괴담이라 운을 띄운 글을 남기지도 않았겠죠?
tomcat_crop.txt 용량은 고작 2KB였습니다.
너무 이상한거에요.
 
 분명 테이블을 마지막으로 백업하고 나서 몇 시간 지나고 초기화할 때 누적된 입력 값은 어림짐작으로 잡아도 그 정도 분량이 아닌데 어째서?
tomcat_crop.txt마지막 줄에 찍힌 시간=(UTC.0) 02:XX
tomcat_A.2022-05-XX.log마지막 줄에 찍힌 시간=(UTC.0) 23:XX
 인스턴스가 여러개였으니 특정 인스턴스에만 연속으로 호출이 걸려 한 동안 로그가 뜸한 타이밍이 가능하긴 하겠죠. 그런데 21시간 연속으로 이 인스턴스가 놀았다고?
이상하잖아요?
 
 
(한정된 자원에서)
가능성이 극도로 낮은건 발생치 않는 걸로 간주하는 것도 방법이다.
 
 
그렇다면 설마 이쪽 명령어에도 용량상 처리 가능한 상한선같은게 있는거였나?
 
 
split -b 500m tomcat_A.2022-05-XX.log tomcat_Apart_
 
 그래서 노트패드++로 열어볼려고 용량 상한선에 맞게 500메가바이트씩 잘랐습니다.(8.3.3 기준)
분할된 파일중 하나를 열어 '현재에서 모두찾기'를 하니깐...
아니 대체 왜?
 tomcat_crop.txt는 비교도 안 될 검색 결과 갯수를 보고 어느 단계에서 오류가 있던것인가로 머리속이 멈출... 뻔했는데 다행히 멈추지는 않고 감속정도?
"검색 결과가 이상해, 리눅스 명령어와 노트패드++중 하나가 나를 속이는거 아닌가?
설마?"
 
grep '도큐먼트_컬럼명' tomcat_Apart_* > tomcat_Agrep.txt
약 10KB 2개, 7KB 1개.
이게 무슨 현상이지?
 
 

뻥쟁이가 누군지는 상관없고, 추출 좀 하게 해줘.
 
 일단 로그파일 원본으로 grep를 해서는 정상적인 추출이 안 되는거 같으니 조금 번거롭더라도 파일 분할과 grep 결과 저장을 해결해봐야겠군요. 그래도 굳이 임시 코드 안 짜도 되는거 어디입니까?
 그리고 파일 분할시 용량 기준으로 자르는건 한줄내로 찍힌 JSON가 중간에 잘릴수도 있어 다른 옵션을 써야겠죠?
 
 
wc -l tomcat_A.2022-05-XX.log
wc -l tomcat_B.2022-05-XX.log
wc -l tomcat_C.2022-05-XX.log
전체 용량이랑 노트패드 용량 제한상 일단 약 3등분이 될 라인수로 해봤습니다.
 
 
split -l 100000 tomcat_A.2022-05-XX.log tomcat_Apart_
grep '도큐먼트_컬럼명' tomcat_Apart_* > tomcat_Agrep.txt
 
그리고 '현재에서 모두찾기'로 나오는 결과 갯수들... 아까보다 늘었났어?
 
 설마? [split -l]에서 라인수를 줄일수록 노트패드에 열어둔 파일들에서 검색되는 결과 갯수가 늘어나서 10만에서 시작한 값은 결국 200까지 가고 100으로 시도해본 결과 더는 늘어나지 않더군요.
 
로그 파일 원본으로 직접 grep = 50개 미만
split&grep로 추출 = 약 5천개
 
???
 무슨 작용인지는 모르겠지만, 입력가능한 JSON입력값이니 일단 텍스트 파일 3개를 합쳐서 정규식으로 불필요한 부분 삭제후 DB에 마저 입력하는걸로 이슈 해결.
 
 

위기는 탈출했다. 그런데 그건 뭐였을까?
 
 
이슈는 해결됐으니 다행이죠.
그런데 말입니다. 그건 대체 무슨 원리였을까요?
 
아무튼 기초들은 언제나 사람을 살리는 치트키이자, 구명 보트입니다.
그리고 테이블에서 수동으로 빼거나 넣을때는 무조건 백업스크립트 실행하는거 잊지마세요.
목차
(1)
 이론
(2)
 TCP
(3)
 소켓


* 주요 참조자료상, 닷넷 기반 TCP/IP, UDP입니다.


 
 
 
1. 이론
 
1) TCP
> TCP클라이언트와 TCP Listener 클래스가 사용된.
 
 
2) UDP
> 연결지향인 TCP와 달리, UDP는 속도를 중시한 처리가 중요한 프로토콜.

* TCP클라이언트/Listener 클래스와 UDP클라이언트 클래스는 내부적으로 소켓 클래스 사용하고, 소켓 클래스는 low-level 소켓 프로그래밍 지원.
* 해당 포스팅에서는 UDP 예시는 생략.





2. TCP


1) 클라이언트 사이드
> 인코딩/디코딩 절차상, 예제의 문자열뿐 아니라 이미지같은 미디어 전송도 가능.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Net.Sockets;
... ... ...
 
//STEP.1    IP주소와 포트번호
TcpClient tClient = new TcpClient("127.0.0.1"80);
 
string requestMsg = "2021_05_17";
byte[] encodedBytes = Encoding.ASCII.GetBytes(requestMsg);//인코딩처리.
 
//STEP.2    NetworkStream
NetworkStream nwStream = tClient.GetStream();
 
//STEP.3    NetworkStream.Write
nwStream.Write(encodedBytes, 0, encodedBytes.Length);
 
//STEP.4    NetworkStream.Read
byte[] recivedBytes = new byte[1024];
int nbytes = nwStream.Read(recivedBytes, 0, recivedBytes.Length);
string resultMsg = Encoding.ASCII.GetString(recivedBytes, 0, nbytes);
 
//STEP.5    접속종료
nwStream.Close();
tClient.Close();
cs


(HTTP 프로토콜처럼)연결 끊길때까지 처리하려면 STEP.4에서

1
2
3
4
5
6
7
8
9
byte[] recivedBytes = new byte[1024];
int nbytes;
MemoryStream mStream = new MemoryStream();
while ((nbytes = nwStream.Read(recivedBytes, 0, recivedBytes.Length)) > 0)
{
     mStream.Write(recivedBytes, 0, nbytes);
}
byte[] resultBytes = mStream.ToArray();
mStream.Close();
cs
MemoryStream으로 루프 돌려야.



2) TCP Listener를 이용한 비동기 서버

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
static void Main(string[] args)
{
  TestTcpAysnc().Wait();
}
... ... ...
async static Task TestTcpAysnc()
{
  int port = 2021;
  IPAddress targetAddress = IPAddress.Parse("127.0.0.1");
 
  TcpListener tListener = new TcpListener(targetAddress, port);
  try
  {
    tListener.Start();
  } catch(SocketException e) {
    //do-something.
  } finally {
    tListener.Stop();
  }
 
  while (true)
  {        
    TcpClient tcpClient = await tListener.AcceptTcpClientAsync().ConfigureAwait(false);
    Task.Factory.StartNew(doStreamAsync, tcpClient);//to new thread
  }
}
 
async static void doStreamAsync(object argObj)
{
  TcpClient tcpClient = (TcpClient)argObj;
  NetworkStream nwStream = tcpClient.GetStream();
 
  String msg = null;
  byte[] buff = new byte[1024];//512, 1024, 2048 ...
  var nBytes = await nwStream.ReadAsync(buff, 0, buff.Length).ConfigureAwait(false);
  if (nBytes > 0)
  {                
    msg = Encoding.ASCII.GetString(buff, 0, nbytes);       
    await nwStream.WriteAsync(buff, 0, nbytes).ConfigureAwait(false);
  }
 
  nwStream.Close();
  tcpClient.Close();
}
cs





 

3. 소켓
 

1) 클라이언트 사이드

> Connect->Send->Receive->Close
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
using System.Net;
using System.Net.Sockets;
... ... ...
public class TestSocket
{
    public static void Main(string[] args)
    {
        int port = 8080;
        Socket socket = null;
        byte[] requestBytes = Encoding.ASCII.GetBytes( string.Empty );
        byte[] responseBytes = new byte[8192];
 
 
        //STEP.1 연결관련 객체
        IPEndPoint endPoint = new IPEndPoint(IPAddress.Parse("172.0.0.1"), port);
        socket = new Socket(endPoint.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
 
        //STEP.2 서버에 연결
        socket.Connect(endPoint);
 
 
        while ((cmd = Console.ReadLine()) != "q")
        {
            requestBytes = Encoding.UTF8.GetBytes(cmd);
 
            try{
                //STEP.3 리퀘스트
                socket.Send(requestBytes, requestBytes.Length, 0);
 
                //STEP.4 리스폰스
                int responseInt = socket.Receive(responseBytes, responseBytes.Length, 0);
                string response = Encoding.UTF8.GetString(responseBytes, 0, responseInt);
                Console.WriteLine(response);
            } catch(SocketException except){
                Console.WriteLine(except.ErrorCode);
                //do-something
            }
        }
 
        //STEP.5
        socket.Close();
    }
}
cs


2) 서버 사이드
> (Bind->Listen->Accept)->(Receive->Send)->Close
// 서버가 블라블라(BLA BLA)하는걸 듣고서 대답하고 닫기.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
using System.Net;
using System.Net.Sockets;
... ... ...
 
namespace TestSocketServer
{
    static void Main(string[] args)
    {
        int port = 8080;
        Socket serverSocket = null;
        Socket clientSocket = null;
        byte[] responseBytes = new byte[8192];
 
 
        //STEP.1 소켓 객체
        IPEndPoint endPoint = new IPEndPoint("172.0.0.1", port);
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 
        try{
            //STEP.2 (포트번호대로)수신 시작.
            serverSocket.Bind(endPoint);
            serverSocket.Listen(10);
 
 
            //STEP.3 새소켓
            clientSocket = serverSocket.Accept();
        } catch(SocketException except){
            Console.WriteLine(except.ErrorCode);
        } catch(ObjectDisposedException except){
            Console.WriteLine(except.ErrorCode);
        }
 
 
        int responseResult = -1;
        string responseString = null;
        while (!Console.KeyAvailable)
        {
            //STEP.4 새소켓으로 수신
            responseResult = clientSocket.Receive(responseBytes);
            responseString = Encoding.UTF8.GetString(responseBytes, 0, responseResult);
 
 
            //STEP.5 답신
            clientSocket.Send(responseBytes, 0, receiveResult, SocketFlags.None);
        }
 
        //STEP.6 소켓 
        clientSocket.Close();
        socket.Close();
    }
}
cs


3) 비동기 소켓 서버
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
using System.Net;
using System.Net.Sockets;
using System.Threading;
... ... ...
 
namespace TestSocketServer
{
    public static ManualResetEvent _threadEvent = new ManualResetEvent(false);
 
    static void Main(string[] args)
    {
        int port = 8080;
        Socket serverSocket = null;
 
 
        //STEP.1 소켓 객체
        IPEndPoint endPoint = new IPEndPoint("172.0.0.1", port);
        serverSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
 
        try{
            //STEP.2 (포트번호대로)수신 시작.
            serverSocket.Bind(endPoint);
            serverSocket.Listen(10);
        } catch(SocketException except){
            Console.WriteLine(except.ErrorCode);
        } catch(ObjectDisposedException except){
            Console.WriteLine(except.ErrorCode);
        }
 
 
        try{
            while (true)
            {
                _threadEvent.Reset();
 
                //STEP.3 클라이언트 요청이 있을때마다 요청 수락
                serverSocket.BeginAccept(SocketCallback, serverSocket);//Accept.시작
 
                _threadEvent.WaitOne();// Wait until a connection is made before continuing.
            }
        } catch(Exception except){
            Console.WriteLine(except.ErrorCode);
        }
    }
 
    private static void SocketCallback(IAsyncResult argResultAsync)
    {
        Console.WriteLine("Accepted");
        Console.ReadLine();
 
        _threadEvent.Set();// Signal the main thread to continue.
 
        Socket listener = (Socket)argResultAsync.AsyncState;
        Socket clientSocket = listener.EndAccept(argResultAsync);//Accept.끝
 
        clientSocket.Close();
    }
}
cs




기타. 참조자료

1) 한국어
C# 프로그래밍 배우기 (Learn C# Programming)
TCP 클라이언트
비동기 TCP 서버

 
목차
1.
 스레딩 기본 제어
2.
 스레드풀






1. 스레딩 기본 제어


1) 생성 및 기본 사용.
> [#웹 컴파일]
> Thread형 오브젝트의 프로퍼티로 IsBackground를 설정후 start하는걸로 포그라운드/백그라운드 처리가능.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
using System;
using System.Text.RegularExpressions;
using System.Threading;
 
namespace Rextester
{
    public class MyThreadClass
    {
        public static int SLEEP_MS = 10000;//10초.
        public static string TIME_FORM = "HH:mm:ss.fff";
        
        public void InstanceFunc(/*함수 인자 및 델레기이트 가능*/)
        {
            try{
                Console.WriteLine("[{0}] Thread {1}", DateTime.Now.ToString(TIME_FORM), Thread.CurrentThread.Name);
                Thread.Sleep(SLEEP_MS);
            }
            catch (ThreadInterruptedException) {
                Console.WriteLine("[{0}] ThreadInterruptedException, '{1}'", DateTime.Now.ToString(TIME_FORM), Thread.CurrentThread.Name);
            }
            catch (ThreadAbortException) {
                 Console.WriteLine("[{0}] ThreadAbortException, '{1}'", DateTime.Now.ToString(TIME_FORM), Thread.CurrentThread.Name);
            }
            
            Console.WriteLine("[{0}] FIN, {1}", DateTime.Now.ToString(TIME_FORM), Thread.CurrentThread.Name);
            /*
            Sleep이 호출되면 지정된 시간만큼 다른 스레드에게 리소스 양도.
            한 스레드가 다른 스레드에서 Thread.Sleep(정적 매소드)는 호출 불가.
            */
        }
        public static void StaticFunc(/*함수 인자 및 델레기이트 가능*/)
        {
            try{
                Console.WriteLine("[{0}] Thread {1}", DateTime.Now.ToString(TIME_FORM), Thread.CurrentThread.Name);
                Thread.Sleep(SLEEP_MS);
            }
            catch (ThreadInterruptedException) {
                Console.WriteLine("[{0}] ThreadInterruptedException, '{1}'", DateTime.Now.ToString(TIME_FORM), Thread.CurrentThread.Name);
            }
            catch (ThreadAbortException) {
                 Console.WriteLine("[{0}] ThreadAbortException, '{1}'", DateTime.Now.ToString(TIME_FORM), Thread.CurrentThread.Name);
            }
            
            Console.WriteLine("[{0}] FIN, {1}", DateTime.Now.ToString(TIME_FORM), Thread.CurrentThread.Name);
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            MyThreadClass threadObj = new MyThreadClass();
            Thread threadForIns = null;
            Thread threadForStatic = null;
            threadForIns = new Thread(new ThreadStart(threadObj.InstanceFunc));            //함수 인자X
            //threadForIns = new Thread(new ThreadStart(threadObj.InstanceFunc(...)));
            threadForIns.Name = "InstanceFunc";
            threadForIns.Start();
 
            Console.WriteLine("[{0}] before Thread.Sleep", DateTime.Now.ToString(MyThreadClass.TIME_FORM));
            Thread.Sleep(MyThreadClass.SLEEP_MS / 5);
            Console.WriteLine("[{0}] before Interrupt", DateTime.Now.ToString(MyThreadClass.TIME_FORM));
            threadForIns.Interrupt();//wake쯤?
 
            //정적 함수이니 [클래스명.함수명]호출.
            threadForStatic = new Thread(new ThreadStart(MyThreadClass.StaticFunc));        //함수 인자X
            //threadForStatic = new new Thread(new ThreadStart(MyThreadClass.StaticFunc(...)));
            threadForStatic.Name = "StaticFunc";
            threadForStatic.Start();
            
            Console.WriteLine("[{0}] before Thread.Sleep", DateTime.Now.ToString(MyThreadClass.TIME_FORM));
            Thread.Sleep(MyThreadClass.SLEEP_MS / 5);
            Console.WriteLine("[{0}] before Abort", DateTime.Now.ToString(MyThreadClass.TIME_FORM));
            threadForStatic.Abort(); //스레드 제거
        }
    }
}
cs

> 실행결과

[15:44:59.345] before Thread.Sleep
[15:44:59.360] Thread InstanceFunc
[15:45:01.377] before Interrupt
[15:45:01.377] ThreadInterruptedException, 'InstanceFunc'
[15:45:01.377] FIN, InstanceFunc
[15:45:01.377] Thread StaticFunc
[15:45:01.377] before Thread.Sleep
[15:45:03.393] before Abort
[15:45:03.410] ThreadAbortException, 'StaticFunc'
 
 
2) 타이머
> System.Threading.Timer과 System.Timers.Timer 2가지가 존재.
[#예제, System.Timers.Timer] //Elapsed에 넣는 static함수는 Task도 허용.
[#예제, System.Threading.Timer] //TimerCallback을 이용한 델리게이트





2. 스레드 풀

1) 최소&최대
> 최대: 스레드 풀에 대기 시킬 작업수는 메모리가 관건이나,
CPU코어당-1개 이상의 스레드
                           1 스레드-최대 N개의 작업 스레드.
> ThreadPool.SetMaxThreads(), ThreadPool.SetMinThreads()로 작업 스레드 갯수 조절.


2) 세부 항목

> 스레드 및 스레딩 사용






기타. 참조자료

1) 한국어
C# 프로그래밍 배우기 (Learn C# Programming)
 
> 관리되는 스레딩 기본사항
> 스레드 및 스레딩 사용
> 스레딩 개체 및 기능
 




기타. 변경이력

일자
 변경이력
 2021-03-28  초안 작성.
목차
(1)
 기본 문법
(2)
 이벤트, 비동기





 

0. 필요한 파일

 

파일명
 예시경로
 VS 2019 [#다운로드]
> 16.3 이상
 (기본경로)
 .NET Core 3.0 [#다운로드]  (기본경로)

* 해당 포스트는 C# 8.0기반이며, VS 업데이트는 16.8.6입니다.
* 해당 포스트는 JAVA/파이썬 같은 다른 프로그래밍 언어를 알고 있는걸 전제로 생략되는 부분이 있습니다.





1. 문법

 

1) 상수류
> 상수 선언시 static불가[#MSDN]
1
2
const int LIMIT_I = 100;//JAVA에서는 final
readonly int m_LIMIT;    //멤버변수에서 선언, 초기화는 클래스 생성자에
cs



2) 배열
> C#에서는 참조 타입

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
//1차원 배열
long[] potions = new long[3];//고정된 크기로 선언.
string[] itemNames = {"체력 회복""스태미나 회복""경험치 물약"};
 
 
//2차원 배열
long[,] eventScores = {{1024512},{100-1}};//선언부터 초기화까지 완료.
 
long[][] eventScoreArr = new long[2][];//가변&2차원 배열
eventScoreArr[0= new long[10];//공간 선언O. 초기화X.
                                //그러니 eventScoreArr[0][0]로 호출시 exception.
eventScoreArr[1= new long[2] {-1-1};//공간 선언O. 초기화O.
 
 
//3차원 배열
long[,,] positions;
cs


3) 문자열
> 정확히는 StringBuilder 클래스를 쓰는 버릇 들이기.
> Immutable 타입 String클래스 Vs. Mutable타입 StringBuilder클래스
[C#] string + string 과 StringBuilder.Append() 차이
C#에서 String과 Stringbuilder를 사용하는 경우
-> 문자열 조합시, String형은 참조타입이라 새로운 객체를 생성 + GC
-> 변경 가능한 StringBuilder는 문자열 붙일때 객체 새로 생성X



4) 연산자
> Null-coalescing operator
> nullable 참조 형식 [#MSDN]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
string fromMsg = null;
string receiveMsg = fromMsg ?? "(기본 메시지)";
/*
if(fromMsg == null)
{
    receiveMsg = "(기본 메시지)";
}
else
{
    //do-nothing.
*/
Console.WriteLine("receiveMsg = "+receiveMsg);
 
int? resultCode = null;//Nullable 적용.
int receiveCode = resultCode ?? -1;
/*
C# 2.0부터 Nullable<int>처리가 가능.
Integer var = null;와 비슷하게 입력값 처리//JAVA
*/
Console.WriteLine("receiveCode = "+receiveCode );
cs
 
 
5) 반복문
> 순차적인 반복문이면 foreach가 권장.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
string[] itemCodes = ...
... ... ...
 
for(int idx=0; idx<itemCodes.Length; idx++)
{
    //do-something.
}
 
 
foreach (tmpItem in itemCodes)
{//CASE. 정방향으로 순차적
    //do-something.
}
 
foreach (tmpItem in itemCodes.Reverse())
{//CASE. 역방향으로 순차적
    //do-something.
}
cs
 
 
6) 네임스페이스
> 네임스페이스내에 클래스 N개 정의 가능.
> 파일명=클래스명이던 JAVA과 비교시, 네임스페이스 지정은 자유로워
: A.cs에 사용한 네임스페이스를 B.cs에도 동일한 네임스페이스 사용가능.
> 네임스페이스내 정의내 정의된 클래스를 가져다쓰려면
using (네임스페이스 경로).클래스명
: (JAVA)  import (패키지 경로).(클래스명)
  (C#)     using  (네임스페이스 경로).(클래스명)
-> 나름 협업 친화적인 규칙?


7) 구조체&클래스
구조체=값 타입(스택) / 클래스=참조 타입(힙)
 C#에서 구조체는 상속불가+인터페이스 가능.
② 클래스 멤버
> 필드변수: 기존의 멤버 변수 그거. + 여기서도 접근제한자 적용.
> 프로퍼티: 클래스 멤버 변수에서 함수처럼 [이름(...)]의 괄호가 없지만,
그 변수내에 get/set 정의 가능.
 
 
8) 함수 파라미터
① Pass by Value
> 함수인자에 변수값을 복사해서 전달.
> 함수에 값을 입력한다고 해서 함수 호출 이후 함수에 넣었던 변수값 변경X
 
② Pass by Reference
> 함수 인자에 ref/out키워드를 덧붙여 사용.
1
2
3
4
5
6
7
8
9
10
11
private void Prefix(ref int argA, out int argB)
{
    ... ... ...
    //do-somethging.
}
 
... ... ...
int codeForRef = 100;
int codeForOut;//초기화 불필요.
Prefix(codeForRef, out codeForOut);
//함수 호출로 codeForRef와 codeForOut의 값 변경된거 확인 가능.
cs
 
③ Named 파라미터
> C# 4.0부터 지원.
> 기존에는 함수 인자 입력시, 정의된 순서대로 값을 입력했다면
함수를 사용할때 key-value처럼 인자를 입력.

④ Optional 파라미터
> C# 4.0부터 지원.
> 자바스크립트에도 있는 함수 인자 기본값 지정
1
2
3
4
5
6
7
8
9
10
private void doFunction(int argA, int argB, int argC=-1)
{
    ... ... ...
    //do-something.
}
 
 
//함수 호출시 입력한 인자가 달라보여도 동일 함수 호출.
doFunction(20212);
doFunction(2021228);
cs
 
⑤ params 키워드
> JAVA로 치면 '가변인수'(Varargs)?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
/*
//Test.java
 
void sendMsg(String... msgs)
{//msgs: 가변 배열인 함수 인자
    ... ... ...
 
sendMsg("test1");
sendMsg("test2_1", "test2_2");
*/
 
private void SendMsg(params string[] msgs)
{
... ... ...
}
 
SendMsg("test1");
SendMsg("test2_1""test2_2");
cs
 
⑥ Partial 키워드
> 다른 코드 파일이여도 동일한 클래스를 작업이 가능케 하는 키워드
 
 
8) 전처리기 지시어
① 조건별 컴파일
1
2
3
4
5
6
7
#if ENV_DEBUG
     //do-something.
#elif ENV_QA
     //do-something.
#else
     //do-something.
#endif
cs
 
② region
1
2
3
4
5
6
7
8
9
10
11
12
/*
함수나 멤버들 그룸 상하에
//START. func.DoXxx
... .... ...
//END. func.DoXxx
수동으로 하던거에 아예 접단 지원하는 지시어.
*/
 
#region Public DoXxx        
public void DoActionA() { }
public void DoActionB() { }        
#endregion
cs
 
 
9) static
> 정적 속성/필드: 프로그램 실행후 소속된 클래스가 처음 사용될때 초기화+동일한 메모리 사용.
> 정적 함수: [클래스명.함수명(...)]으로 호출&사용. 인스턴스 객체(혹은 객체화된 클래스)에서 호출 불가.
> 정적 클래스: 생성자 X, 객체화 불가 (싱글톤이 아닌 Utility용 클래스에 쓰던거와 동일)
 
 
10) 제네릭
> 클래스명<T>
: HashMap<>선언해서 쓸때 다양한 타입 허용하는 그거. (용어도 동일)
 
 
11) 익명타입 변수
> C# 3.0부터 지원.
> 변수 자료형을 var로 쓰는건데, C#에서는 읽기전용이라 함.
 
 
12) 확장 매소드(확장 함수)
> C# 3.0부터 지원.
> 굳이 String/Integer 클래스를 상속받아 추가 기능을 넣을 필요없이,
[네임스페이스.정적_클래스.정적_함수]로 정의해서
필요한곳에서 접착해서 사용.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
//    확장 매소드(or 확장 함수)가 정의된 CS파일.
namespace Com.Muteukgi.ExtensionMethods
{
    public static class ExtenedFunction
    {
        public static int DoActionType1Ext(this int arg1)
        {
            int result = -1;
            //do-something.
            return result;
        }
        public static int DoActionType2Ext(this int arg1, int arg2)
        {
            int result = -1;
            //do-something.
            return result;
        }
    }
}
 
//다른 CS파일에서
using Com.Muteukgi.ExtensionMethods;
 
... ... ...
    int test = 100;
    int tmp = -1;
    tmp = test.DoActionType1Ext();
    tmp = test.DoActionType2Ext(2021);
cs
 
 
 
 
 
2. 이벤트, 비동기
 
1) 이벤트 핸들러
1
2
3
4
5
6
7
8
9
10
11
    ... ... ...
    xxx.Click += new EventHandler( ActionHandler );//핸들러 등록
    ... ... ...
    ... ... ...
    xxx.Click -= new EventHandler( ActionHandler );//핸들러 해제.
}
 
private void ActionHandler (object sender, EventArgs e)
{
    //do-something.
}
cs
 
 
2) 델리게이트(delegate)
> 예시나 설명을 보면,
: 상위 기능 함수(?) 내부에서 입력값별로 if-else나눈 블록에서 호출되는 세부 기능을 하는 함수 호출하는걸 도와주는 격.
: 상위 함수의 함수 인자가 delegate로 선언된 변수이면 그 인자에는 함수명을 변수처럼 입력하고, 상위 함수 내에서는 delegate형 변수는 코드상 입력된 함수의 코드대로 작동.
//지명된 다른 매소드 이름에 맞게 매소드 연기.
> 함수를 참조하는 변수로 함수 포인터랑 비슷하다는 설명도 있음.
: 함수 포인터 비슷하다 하지만, 델리게이트 1개-함수1개 이상 가능.(델레게이트 체인)
: 델리게이트 호출시 체인이 걸린 함수들이 순차적?동시에 실행? //세부 동작 확인 필요.
> 인스턴스/정적 함수 모두 가능.
> 제네릭 타입도 허용.
> 실질적인 용도는 콜백 매소드라 함.
 
 
3) 무명 함수(혹은 익명 함수)
> 이벤트 핸들러등에서 반복 사용될게 없는 코드를 함수 선언해서 쓰지않고, 내부 동작 자체를 그대로 붙여넣음.
1
2
3
4
5
6
7
8
9
10
11
/*
EventHandler에 쓰이는 함수의 인자는 (object sender, EventArgs e)인데,
익명형은 경우에 따라 함수인자도 생략된 형태O
*/
xxx.Click += new EventHandler(delegate(object s, EventArgs a) { /* do-something. */ });
 
xxx.Click += (EventHandler) delegate(object s, EventArgs a) { /* do-something. */ };
 
xxx.Click += delegate(object s, EventArgs a) { /* do-something. */ };
 
xxx.Click += delegate { /* do-something. */ };
cs
 
 
4) 람다
1
2
3
4
5
//type1.
(argA, argB) => {    /* do-something */    };
 
//type2.
(int argA, string argB) => {    /* do-something */    };
cs
 
 
5) async&await
> C# 5.0부터 지원된 비동기 관련 기능.
: 비동기 함수에도 반환값 처리 가능(JAVA로 치면 Future로 스레드에서 반환값 받는거.)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/*
In JAVA
ExecutorService service = Executors.newSingleThreadExecutor();
Future<T> future = service.submit(task);
REF. https://deep-dive-dev.tistory.com/6
*/
    ... ... ...
    Debug.WriteLine(Thread.CurrentThread.ManagedThreadId);
    DoAction();
}
 
private async void DoAction()
{    
    Debug.WriteLine("DoAction(1) " + Thread.CurrentThread.ManagedThreadId);
    int tmp = -1;
 
    tmp = await DoAsync(10);                //async 키워드 있는 함수로 비동기 처리
    Debug.WriteLine("tmp: " + tmp );
 
    var testTask = Task.Run( ()=>DoFunc(10) );//async 키워드 없는 함수로 비동기 처리
    tmp = await testTask;
    Debug.WriteLine("tmp: " + tmp );
    Debug.WriteLine("DoAction(2) " + Thread.CurrentThread.ManagedThreadId);
}
 
private async Task<int> DoAsync(int arg)
{
    Debug.WriteLine("DoAsync, " + Thread.CurrentThread.ManagedThreadId);
    ... ... ...
    return result;
}
 
private int DoFunc(int arg)
{
    ... ... ...
    return result;
}
cs
 
 
 
 

기타. 참조자료

1) 한국어
.NET Core 3.0의 새로운 기능 | Microsoft Docs
[C#] 왜 partial class를 사용하는걸까요?
 

 

ajax, js

* 자바스크립트, 크로스 도메인 이슈 대응중 하나인 JSONP 방식 예제입니다.

* JSONP방식은 GET통신만 지원합니다.






예제


1) 자바스크립트, request

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
document.write("<script src='https://code.jquery.com/jquery-1.11.2.min.js'><"+"/script>"); 
var g_url = "https://domain_B";
var sendObject = {
       param1: "thisIsJsonp",
       param2: "withGet"
    };
    
jQuery.ajax({
    dataType: "jsonp",
    url : g_url,
    type: "GET",
    data: sendObject,
    
    jsonp: "jsonpCallBack",
    success: function (msg)
    {
        console.log(msg);
        alert("성공!");
    },
    error : function (msg, status, err)
    {
        console.log("status=" + status);
        console.log("msg=" + msg);
        alert('실패!');
    }
 });
cs



2) 자바, 서블릿

-> 리스폰스를 넣을때 리퀘스트에 있던 콜백함수명으로 입력치 않으면 'JSONP response is not defined'쪽 문제가 발생합니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import java.io.IOException;
import java.io.PrintWriter;
 
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
 
... ... ...
 
public class AdvenceReservation extends HttpServlet
{
    @Override
    protected void doGet(HttpServletRequest servletRequest, HttpServletResponse servletResponse) throws ServletException, IOException
    {
        String param1 = servletRequest.getParameter("param1");
        String param2 = servletRequest.getParameter("param2");
        ... ... ...
        String callBack = servletRequest.getParameter("jsonpCallBack");
        
        PrintWriter writer = servletResponse.getWriter();
        writer.write(callBack +"( {\"result\":\"ok\"} )");
        writer.flush();
        writer.close();
    }
}
cs






참조자료.


[Ajax] JSONP를 이용하여 Cross Domain 해결 :: 개발은 전투다

javascript - AJAX with JSONP returns error Callback is Undefined - Stack Overflow






기타. 변경이력


일자

변경이력

2019-10-27

 초안

'프로그래밍note > 스크립트 계열' 카테고리의 다른 글

JS ajax: jsonp  (0) 2019.10.27
루아: 기초정리(2)  (0) 2019.06.06
루아: 기초정리(1)  (0) 2019.05.26
HTML5: cocos2d-js 기초 (1)  (0) 2018.05.23
파이썬3: 기초정리(2)  (0) 2018.05.18
파이썬3: 기초정리(1)  (0) 2018.05.14





1. 기초+이론


1) 특징

> JSON형태 구조(DOCUMENT)

> 데이터 입력시, objectId라는 필드가 자동생성되며 해당 컬럼에는 중복되지 않는 유일한 값이 들어간다.

: [_id]라는 필드로 존재하고, 12바이트짜리 16진수.

예. ObjectId(5d3d8f39f450a8969574d2e1) [16]

    1글자->1바이트(8비트)

               2진수 8칸(?)->16진수 2칸(?)이므로

5d3d8f39[16]

 f450a8[16]

 9695[16]

 74d2e1[16]

 타임스탬프, 4바이트

 머신 ID, 3바이트

 DB서버 Pid, 2바이트

 순차번호, 3바이트

> db에 입력되는 DOCUMENT 그룹을 collection이라고 부름.

: 동일한 collection에 다수의 데이터 입력가능.

> NoSQL이여서 불필요한 Join 최소화 가능+고정된 스키마X.






2. 설치


1) 필요한 파일

파일명

예시 경로

 MongoDB Community Server [#4.0.11]

 D:\dev_lib\mongodb4



2) 환경변수


3) 실행

-> 서비스 등록없이 예제 테스트만 하려는 목적이라 수동 실행입니다.

-> 총 2개의 프롬프트 창을 띄우게 됩니다.

D:\dev_lib\mongodb4\save 경로로 폴더 생성후 DB 저장 경로 지정+실행

1
2
3
4
5
6
7
8
9
10
11
12
13
D:\dev_lib\mongodb4\bin>mongod --dbpath "D:\dev_lib\mongodb4\save"
2019-07-28T20:48:03.680+0900 I CONTROL  [main] Automatically disabling TLS 1.0, to force-enable TLS 1.0 specify --sslDisabledProtocols 'none'
2019-07-28T20:48:03.683+0900 I CONTROL  [initandlisten] MongoDB starting : pid=13340 port=27017 dbpath=D:\dev_lib\mongodb4\save 64-bit host=ANALOG-GREEN
2019-07-28T20:48:03.683+0900 I CONTROL  [initandlisten] targetMinOS: Windows 7/Windows Server 2008 R2
2019-07-28T20:48:03.684+0900 I CONTROL  [initandlisten] db version v4.0.11
... ... ...
2019-07-28T20:48:03.686+0900 I CONTROL  [initandlisten] options: { storage: { dbPath: "D:\dev_lib\mongodb4\save" } }
... ... ...
2019-07-28T20:48:04.982+0900 I FTDC     [initandlisten] Initializing full-time diagnostic data capture with directory 'D:/dev_lib/mongodb4/save/diagnostic.data'
... ... ...
2019-07-28T20:48:05.338+0900 I INDEX    [LogicalSessionCacheRefresh]     building index using bulk method; build may temporarily use up to 500 megabytes of RAM
2019-07-28T20:48:05.346+0900 I INDEX    [LogicalSessionCacheRefresh] build index done.  scanned 0 total records. 0 secs
2019-07-28T20:48:05.346+0900 I COMMAND  [LogicalSessionCacheRefresh] command config.$cmd command: createIndexes { createIndexes: "system.sessions", indexes: [ { key: { lastUse: 1 }, name: "lsidTTLIndex", expireAfterSeconds: 1800 } ], $db: "config" } numYields:0 reslen:114 locks:{ Global: { acquireCount: { r: 2, w: 2 } }, Database: { acquireCount: { w: 2, W: 1 } }, Collection: { acquireCount: { w: 2 } } } storage:{} protocol:op_msg 361ms
cs


제 경우, [http://localhost:27017]로 접속시
[It looks like you are trying to access MongoDB over HTTP on the native driver port.]

라는 메시지가 브라우저에 출력됩니다.

프롬프트 창을 1개 더 띄워고서

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
D:\dev_lib\mongodb4\bin>mongo
MongoDB shell version v4.0.11
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
... ... ...
MongoDB server version: 4.0.11
Server has startup warnings:
2019-07-28T20:53:20.267+0900 I CONTROL  [initandlisten]
2019-07-28T20:53:20.267+0900 I CONTROL  [initandlisten] ** WARNING: Access control is not enabled for the database.
2019-07-28T20:53:20.267+0900 I CONTROL  [initandlisten] **          Read and write access to data and configuration is unrestricted.
2019-07-28T20:53:20.267+0900 I CONTROL  [initandlisten]
2019-07-28T20:53:20.268+0900 I CONTROL  [initandlisten] ** WARNING: This server is bound to localhost.
2019-07-28T20:53:20.268+0900 I CONTROL  [initandlisten] **          Remote systems will be unable to connect to this server.
2019-07-28T20:53:20.268+0900 I CONTROL  [initandlisten] **          Start the server with --bind_ip <address> to specify which IP
2019-07-28T20:53:20.268+0900 I CONTROL  [initandlisten] **          addresses it should serve responses from, or with --bind_ip_all to
2019-07-28T20:53:20.268+0900 I CONTROL  [initandlisten] **          bind to all interfaces. If this behavior is desired, start the
2019-07-28T20:53:20.268+0900 I CONTROL  [initandlisten] **          server with --bind_ip 127.0.0.1 to disable this warning.
2019-07-28T20:53:20.268+0900 I CONTROL  [initandlisten]
---
Enable MongoDB's free cloud-based monitoring service, which will then receive and display
metrics about your deployment (disk utilization, CPU, operation statistics, etc).
The monitoring data will be available on a MongoDB website with a unique URL accessible to you
and anyone you share the URL with. MongoDB may use this information to make product
improvements and to suggest MongoDB products and deployment options to you.
To enable free monitoring, run the following command: db.enableFreeMonitoring()
To permanently disable this reminder, run the following command: db.disableFreeMonitoring()
---
>
cs






4. 예제


1) 기본 명령어

1
2
3
4
5
6
7
8
9
10
11
// INSERT.
> db.sampleCollec.insert(
... {
... date:"2019-07-28",
... writer:"MuTeukGi",
... title:"sample insert"
... }
... )
WriteResult({ "nInserted" : 1 })
> db.sampleCollec.find()
"_id" : ObjectId("5d3d8f39f450a8969574d2e1"), "date" : "2019-07-28""writer" : "MuTeukGi""title" : "sample insert" }
cs


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// UPDATE.
> db.sampleCollec.insert(
... {
... date:"2019-07-28",
... writer:"MuTeukGi",
... title:"sample 2",
... tag:"data 2"
... }
... )
WriteResult({ "nInserted" : 1 })
> db.sampleCollec.find()
"_id" : ObjectId("5d3d8f39f450a8969574d2e1"), "date" : "2019-07-28""writer" : "MuTeukGi""title" : "sample insert" }
"_id" : ObjectId("5d3d9325f450a8969574d2e2"), "date" : "2019-07-28""writer" : "MuTeukGi""title" : "sample 2""tag" : "data 2" }
> db.sampleCollec.update(
... {title:"sample 2"},  //조건문.(in SQL, where)
... {$set: {ver:1}} //update 쿼리문
... )
WriteResult({ "nMatched" : 1"nUpserted" : 0"nModified" : 1 })
> db.sampleCollec.find()
"_id" : ObjectId("5d3d8f39f450a8969574d2e1"), "date" : "2019-07-28""writer" : "MuTeukGi""title" : "sample insert" }
"_id" : ObjectId("5d3d9325f450a8969574d2e2"), "date" : "2019-07-28""writer" : "MuTeukGi""title" : "sample 2""tag" : "data 2""ver" : 1 }
cs


dynamoDB와 달리 update시, 조건문을 이용해서 N개의 아이템에서 특정 컬럼값만 수정되게 지정이 가능하고,

$set, $pull등 특정 컬럼값 수정에 대한 처리가 가능.






기타. 참조자료


01. MongoDB(몽고디비) Study - NoSQL 이란? 그리고 MongoDB 소개

[MongoDB] 강좌 1편: 소개, 설치 및 데이터 모델링 | VELOPERT.LOG


조대협의 블로그 :: MongoDB 30분만에 이해하기.. (설치,테스트 및 자바 샘플)

[mongoDB] mongoDB 설치하기 / 환경설정 - windows - DWFOX

몽고디비 윈도우 설치 (zip 파일) : 네이버 블로그


MongoDB CRUD Operations — MongoDB Manual

[MongoDB] 강좌 5편 Document 수정 – update() 메소드 | VELOPERT.LOG






기타. 변경이력


일자

변경이력

2019-07-28

 초안.







0. 시작하기 전


해당 포스트는

Android Developers Blog: An update on Eclipse Android Developer Tools등으로 이클립스 지원 중단이 발표되면서
기존 이클립스+ADT 프로젝트를 안드로이드 스튜디오용으로 컨버팅하는 작업말고도
추가 개발을 하고자 조금이라도 빠른 적응을 위한 가이드글입니다.


즉, 개발 초심/입문자를 위한것이 아니라
기존에 안드로이드 개발을 하던 사람 기준으로 개발도구 전환을 하는데 촛점을 두고 있으므로
초보자분에겐 안 맞을수도 있습니다.






1. gradle


1) gradle이란?

빌드 및 의존성 처리를 위한 스크립트 파일입니다.

어떻게 보면 이클립스+ADT환경이 GUI에 가까웠다면 gradle을 쓰는건 CUI에 가까운 방식?
(혹은 용도별로 bat파일을 다 작성/설정하는 모양새)



2) setting.gradle

1
include ‘:app’
cs


스트립트 내용자체는 굉장히 단순합니다.
물론 개발목적/요구사항에 따라 커스텀 마이징은 있겠지만, 구글쪽 문서나 해당 스크립트가 취급하는 내용을 보면
코드를 변경할 경우는 많지 않은것으로 보입니다.



3) build.gradle

> 안드로이드 스튜디오에서 build.gradle은 2가지가 있습니다.

: 여기서는 임의로 프로젝트 build.gradle / app build.gradle(혹은 모듈 레벨) 표기하겠습니다.

: 간단히만 짚으면, 구글링해서 샘플코드를 보다가 무슨 build.gradle인지 분간이 안 되면
app build.gradle --> apply plugin로 시작 혹은 android, buildTypes항목 여부.

프로젝트 build.gradle --> repositories 항목
으로 구별해볼수 있습니다.

> app build.gradle.

: 이클립스+ADT에서 주로 쓰던 AndroidManifest.xml에 명시하던 내역이 있습니다.

[(프로젝트폴더)\app\src\main\AndroidManifest.xml]

: 추가 라이브러리 연동 및 설정가능.

> 프로젝트 build.gradle

: 구글링 하다가 build.gradle을 편집하라는 내용이 있는데 대부분 이쪽이 gradle은 아니던거 같습니다.





2. 매니페스트(manifest.xml) 연계


app build.gradle쪽에서

연동된 라이브러리/minSdkVersion/버전코드 및 네임등을 지정가능하니
여기선 깔끔한 코드를 위해 액티비티 등록과 시스템 권한 지정으로 역할을 나누는게 나을것 같습니다.

앱 구동상 어느쪽을 읽는게 우선인지로 성능에 의미있는 차이를 있다면 이야기가 좀 다르겠지만요.






3. 리소스 경로 관리


1) 경로

* xml, 이미지파일, raw 하위경로 파일등은 대문자를 쓰면 안됩니다.
이건 이클립스+ADT때도 허용되지 않던 명명규칙이라 그대로 유지인거 같습니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
android {
... ... ...
    sourceSets {
        main {
            manifest.srcFile 'src/main/AndroidManifest.xml'
            java.srcDirs = ['src/main/java']
            res.srcDirs = ['src/main/res',
                           'src/main/res/raw',
                           'src/main/res/layouts/activity',
                           'src/main/res/layouts/dialog',
                           'src/main/res/layouts/widget',
                           'src/main/res/layouts/view'
            ]
        }
    }
}
cs


예시.



2) 파일IO

> assets디렉토리쪽은 리소스 ID가 아닌 AssetManager만 가능하다고 합니다.

1
2
3
4
5
6
7
8
9
10
11
try {//raw폴더 하위경로 기준.
    InputStream ins = argContext.getResources().openRawResource(R.raw.my_raw_file_name);
    byte[] buffer = new byte[is.available()];
    ins.read(buffer);
    ins.close();
    
    Log.d("SAMPLE"new String(buffer));
    } catch (IOException exception) {
        exception.printStackTrace();
    }
}
cs






4. 빌드&디버그


1) 빌드

빌드메뉴의 표기명이 좀 헷갈리더군요.

Build Bundle(s) / APK(s)면 서명이 없는 테스트용 APK고

Generate Signed Bundle / APK가 마켓에 업로드 가능한 빌드메뉴입니다.

서명파일 작성 및 암호입력은 이클립스 기반과 동일합니다.

옆의 Run을 보시면 디버그 모드가 있으니


이클립스 기반에서는 써본적이 없는데,

안드로이드 스튜디오에서는 디버그 모드로 실행하면 break point를 거는게 지원되네요.

안드로이드 개발용 에뮬레이터는 너무 느리니 NOX와 연동해서 디버깅하는거 추천드립니다.



2) 디버깅

주요 구간마다 로그를 심을때 제일 번거로운게 로그가 있는 함수명도 기입하는거였는데

구글링 해보니 로그를 심은 함수의 이름과 호출된 코드라인번호까지 알수 있는 방법이 있더군요.

코드는 아래의 링크로 대체합니다.

> [안드로이드/Android]개발할때만 Log 남기는 방법 - Debug Log - 박상권의 삽질블로그






기타. 참조자료


빌드 구성  |  Android Developers

Gradle 팁 및 레시피  |  Android Developers

리소스 제공  |  Android Developers


[Android] Gradle 이란?



장범석님의 개발일지: [안드로이드] Gradle로 여러가지 버전 생성하기

안드로이드 layout폴더를 서브폴더로 나누기 :: 혁준 블로그






기타. 변경이력


일자

변경이력

2019-07-01

 초안


mapper.writevalueasstring recursive

  OR
xxx.xxx["json"]->xxx.xxx["json"]->xxx.xxx["json"]->xxx.xxx["json"]
  OR
chain reference등으로


mapper.writeValueAsString(this);로 스트링으로 뽑아서 볼려고 할때 발생한 버그로

원래 mapper.writeValueAsString를 사용하는데 별 문제가 없었지만,

함수명을 명명법에 맞춰 고치는 과정에서 문제가 발생했습니다.





해법.


//정상 작동 버전

publuc String convertJson()

{

   mapper.writeValueAsString(this);

}

//문제가 발생 버전

publuc String getJson()

{

   mapper.writeValueAsString(this);

}


에러로그에서 xxx.xxx["json"]는
다른 스택오버플로우 글에서는 클래스 멤버 변수가 기입된 케이스들인데, 제 코드에서는 json이라는 멤버 변수가 없습니다.

jackson mapper에 흔한 버그(common bug)라고 나왔지만,

잠시 짚고 넘어갈것이

jackson은 기본적으로 멤버 변수를 getter/setter를 생성해둡니다.


즉, 제 경우에는

json내 필드명과 무관한 함수명에 get/set류를 사용해서 발생한겁니다.

함수의 접두어를 다른걸 쓰니 정상가동 확인됐습니다.






기타. 변경이력


일자

변경이력

2019-06-30

 초안


* 해당 포스트는
우분투 16.04.4 LTS기준이며 아파치+mySQL조합입니다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
root@ubuntu# cat /etc/*release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=16.04
DISTRIB_CODENAME=xenial
DISTRIB_DESCRIPTION="Ubuntu 16.04.4 LTS"
NAME="Ubuntu"
VERSION="16.04.4 LTS (Xenial Xerus)"
ID=ubuntu
ID_LIKE=debian
PRETTY_NAME="Ubuntu 16.04.4 LTS"
VERSION_ID="16.04"
HOME_URL="http://www.ubuntu.com/"
SUPPORT_URL="http://help.ubuntu.com/"
BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"
VERSION_CODENAME=xenial
UBUNTU_CODENAME=xenial
cs






1. 아파치


1) 설치

1
2
root@ubuntu# sudo apt update
root@ubuntu# sudo apt install apache2 libapache2-mod-passenger
cs



2) 방화벽

-> 웹 브라우저로 로컬호스트 혹은 AWS 인스턴스 주소로 접속이 안 될 경우입니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
root@ubuntu# service apache2 restart
root@ubuntu# /etc/init.d/apache2 restart
[ ok ] Restarting apache2 (via systemctl): apache2.service.
root@ubuntu# netstat -atp |grep apache
tcp6       0      0 [::]:http               [::]:*                  LISTEN      12743/apache2
root@ubuntu# netstat  -ln
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address           Foreign Address         State
tcp        0      0 0.0.0.0:22              0.0.0.0:*               LISTEN
tcp        0      0 0.0.0.0:3690            0.0.0.0:*               LISTEN
tcp6       0      0 :::22                   :::*                    LISTEN
tcp6       0      0 :::80                   :::*                    LISTEN
udp        0      0 0.0.0.0:68              0.0.0.0:*
Active UNIX domain sockets (only servers)
Proto RefCnt Flags       Type       State         I-Node   Path
unix  2      [ ACC ]     STREAM     LISTENING     18891    /run/user/1000/systemd/private
unix  2      [ ACC ]     SEQPACKET  LISTENING     8806     /run/udev/control
unix  2      [ ACC ]     STREAM     LISTENING     13293    /run/uuidd/request
unix  2      [ ACC ]     STREAM     LISTENING     13298    /var/lib/lxd/unix.socket
unix  2      [ ACC ]     STREAM     LISTENING     22157    /run/snapd.socket
unix  2      [ ACC ]     STREAM     LISTENING     22158    /run/snapd-snap.socket
unix  2      [ ACC ]     STREAM     LISTENING     13296    /run/acpid.socket
unix  2      [ ACC ]     STREAM     LISTENING     13297    /var/run/dbus/system_bus_socket
unix  2      [ ACC ]     STREAM     LISTENING     32866    /tmp/passenger.bOlfeib/agents.s/watchdog_api
unix  2      [ ACC ]     STREAM     LISTENING     32911    /tmp/passenger.bOlfeib/agents.s/core
unix  2      [ ACC ]     STREAM     LISTENING     32912    /tmp/passenger.bOlfeib/agents.s/core_api
unix  2      [ ACC ]     STREAM     LISTENING     32962    /tmp/passenger.bOlfeib/agents.s/ust_router
unix  2      [ ACC ]     STREAM     LISTENING     15091    @ISCSIADM_ABSTRACT_NAMESPACE
unix  2      [ ACC ]     STREAM     LISTENING     32963    /tmp/passenger.bOlfeib/agents.s/ust_router_api
unix  2      [ ACC ]     STREAM     LISTENING     8796     /run/systemd/private
unix  2      [ ACC ]     STREAM     LISTENING     8807     /run/lvm/lvmetad.socket
unix  2      [ ACC ]     STREAM     LISTENING     8813     /run/lvm/lvmpolld.socket
unix  2      [ ACC ]     STREAM     LISTENING     8814     /run/systemd/journal/stdout
root@ubuntu# netstat -atp |grep apache
tcp6       0      0 [::]:http               [::]:*                  LISTEN      12743/apache2
root@ubuntu# iptables -I INPUT 1 -p tcp --dport 80 -j ACCEPT
root@ubuntu# /etc/init.d/apache2 restart
[ ok ] Restarting apache2 (via systemctl): apache2.service.
cs






2. mySQL & 레드마인


1) mySQL설치.

1
2
3
4
root@ubuntu# apt-get install mysql-server mysql-client
//설치시 mySQL-root계정 비번 입력
this_is_mySQL_password_2019
... ... ...
cs



2) 레드마인&레드마인-mysql

1
2
3
4
5
6
7
root@ubuntu# apt-get install redmine redmine-mysql
... ... ...
MySQL application password for redmine/instances/default:
this_is_mySQL_password_2019
... ... ...
Setting up sqlite3 (3.11.0-1ubuntu1.1) ...
Processing triggers for libc-bin (2.23-0ubuntu10) ...
cs



3) 확인

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
ubuntu# sudo mysql -u root -p
Enter password: this_is_mySQL_password_2019
Welcome to the MySQL monitor.  Commands end with ; or \g.
Your MySQL connection id is 14
Server version: 5.7.25-0ubuntu0.16.04.2 (Ubuntu)
 
Copyright (c) 20002019, Oracle and/or its affiliates. All rights reserved.
 
Oracle is a registered trademark of Oracle Corporation and/or its
affiliates. Other names may be trademarks of their respective
owners.
 
Type 'help;' or '\h' for help. Type '\c' to clear the current input statement.
 
mysql> SHOW DATABASES;
+--------------------+
| Database           |
+--------------------+
| information_schema |
| mysql              |
| performance_schema |
| redmine_default    |
| sys                |
+--------------------+
5 rows in set (0.00 sec)
cs






3. gem


1) 설치

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@ubuntu# ruby -v    //<--원래는 없는데, 아파치 이후에 자동으로 있음.
ruby 2.3.1p112 (2016-04-26) [x86_64-linux-gnu]
 
root@ubuntu# sudo gem update
... ... ...
Updating web-console
ERROR:  Error installing web-console:
        Unresolved dependency found during sorting - activesupport (>= 4.2.0) (requested by rails-dom-testing-2.0.3)
Gems updated: actionmailer actionpack actionview activejob activesupport concurrent-ruby erubi rack rails-dom-testing actionpack-action_caching actionpack-xml_parser method_source railties activemodel activerecord arel addressable public_suffix atomic awesome_nested_set bigdecimal binding_of_caller builder bundler byebug coderay coffee-rails coffee-script-source css_parser debug_inspector did_you_mean eventmachine execjs ffi globalid hike htmlentities i18n io-console jbuilder jquery-rails json listen rb-fsevent ruby_dep crass loofah mail mini_mime mime-types mime-types-data minitest molinillo multi_json mysql2 connection_pool net-http-persistent net-ldap net-telnet mini_portile2 nokogiri oj polyglot power_assert protected_attributes psych rack-test nio4r rails-html-sanitizer rails-observers rake rb-inotify rbpdf rbpdf-font rdoc redcarpet request_store rmagick nokogiri roadie-rails sass sass-listen sass-rails sdoc spring sprockets sprockets-rails sqlite3 test-unit thor thread_safe tilt treetop turbolinks turbolinks-source tzinfo uglifie
root@ubuntu# gem install -f web-console
root@ubuntu# sudo gem update
... ... ...
root@ubuntu# sudo gem install bundler
Successfully installed bundler-2.0.2
Parsing documentation for bundler-2.0.2
Done installing documentation for bundler after 2 seconds
1 gem installed
cs






4. conf


1) passenger.conf

1
2
3
4
5
6
7
8
9
10
root@ubuntu# sudo ln -s /usr/share/redmine/public /var/www/html/redmine
root@ubuntu# sudo touch /usr/share/redmine/Gemfile.lock
root@ubuntu# sudo chown www-data:www-data /usr/share/redmine/Gemfile.lock
root@ubuntu# vi /etc/apache2/mods-available/passenger.conf
<IfModule mod_passenger.c>
  PassengerRoot /usr/lib/ruby/vendor_ruby/phusion_passenger/locations.ini
  PassengerDefaultRuby /usr/bin/ruby
  PassengerDefaultUser www-data            //<--추가항목
</IfModule>
:wq
cs



2) redmine.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
root@ubuntu# vi /etc/apache2/sites-available/redmine.conf    //<--신규생성
<VirtualHost *:80>
     ServerAdmin admin@example.com
     DocumentRoot /var/www/html/redmine
     ServerName xxxxxxxxxxxxxxxxxxxxx.compute.amazonaws.com
     ServerAlias www.xxxxxxxxxxxxxxxxxxxxx.compute.amazonaws.com
 
     <Directory /var/www/html/redmine>
         RailsBaseURI /redmine
         PassengerResolveSymlinksInDocumentRoot on
     </Directory>
 
     ErrorLog ${APACHE_LOG_DIR}/error.log
     CustomLog ${APACHE_LOG_DIR}/access.log combined
 
</VirtualHost>
:wq
cs



3) 서비스 실행.

1
2
3
4
5
6
7
root@ubuntu# sudo a2ensite redmine.conf  //반대는 a2dissite  redmine.conf
sudo a2ensite redmine.conf
Enabling site redmine.
To activate the new configuration, you need to run:
  service apache2 reload
root@ubuntu# service apache2 reload
root@ubuntu# sudo systemctl restart apache2.service
cs






기타. 변경이력


일자

변경이력

2019-06-30

 초안

+ Recent posts