[달빛조각사] 달빛 조각사 서버

 

 

현재 사전 예약을 받고 있는 달빛 조각사의 서버가

 

엘릭서(Elixir)로 만들어 졌다고 한다.

 

출처: 네이버 포스트

 

https://m.post.naver.com/viewer/postView.nhn?volumeNo=19418083&memberNo=11255530

 

송재경 개발 ‘달빛조각사’, NDC에서 첫 정보공개

[BY 게임인사이트] 엑스엘게임즈가 개발 중인 모바일 MMORPG 달빛조각사가 NDC에서 모습을 드러냈다. 201...

m.post.naver.com

.

 

 

판타지 소설 좋아하는 사람들한테 엘릭서는

 

능력치를 영구히 증가시켜주거나 반시체도 살려내는 마법의 물약으로

 

알고 있는 사람이 더 많을텐데 

 

엘릭서(Elixir)는 얼랭(Erlang) 가상머신(VM) 위에서 동작하는

 

함수형, 동시성 프로그래밍 언어이다.

 

 

 

 

 

2019 개발자 컨퍼런스(NDC)에서 공개한 내용이라고 하는데

 

와 엘릭서로 게임서버를 만들줄이야..

 

저거 서버 뻑나거나 서버 개발자 그만둬버리면

 

다음 개발자 뽑기 좀 빡셀 것 같다

 

엘릭서가 유명한 언어긴 하지만 다른 언어들에 비해서

 

능숙히 다룰줄 아는 사람이 적은데 ㄷㄷ

 

문제는 달빛조각사를 개발한

 

엑스엘게임즈의 김민욱 서버개발 담당자 조차 

 

달빛조각사를 개발하면서 처음으로 접한 언어라고 한다;;

 

물론 아주 대단하신 개발자 분이시고 경험도 많으시니 큰 문제는 생기지 않겠지만

 

음... 사실 개발자 입장에서 처음 접한 언어로의 개발은 가능하긴 한데

 

갑자기 생긴 이슈에 대해선 대처 속도가 느릴 수 밖에 없기에

 

오픈하거나 업데이트 이후 발생하는 오류 처리 속도(여기선 정기점검의 연장 가능성)를

 

얼마나 빠르게 제어할 수 있을지가 궁금하다.

 

 

포스팅을 읽어 보니

 

또한 자동 코딩 포맷팅 기능과 더불어 빠른 컴파일 속도, 경량화된 프로세스(ligh-weight process), 마이크로 서비스, 빠른 이터레이션, 메모리 문제로부터의 해방(단, 메모릭 릭은 해결하지 못함), 타이밍 문제로 인한 고생이 덜하다는 것 등이 강점이다.

 

 

라는 문구가 있는데 저기서 - 메모리 릭은 해결하지 못함 -  이부분이... ㄷㄷㄷ

 

저 포스팅은 올해 4월에 포스팅된 글이니 지금은 아마 전부 해결 했겠지 하는 믿음을 가져 본다...;;;

 

 

마지막으로

 

엘릭서는 높은 가용성을 가지고 있고 높은 생산성을 가지고 있는 언어 이기 때문에

 

초기 단계에 있는 게임의 빠른 대규모 업데이트라던지 새로운 컨텐츠의 꾸준한 증가를 기대 해 볼 수 있을 것 같다.

 

 

 

 

 

[안드로이드] FCM 웹(JAVA)서버에서 푸시메세지 보내기


안드로이드의 기존 GCM에서 FCM으로 바뀌었다


구글 클라우드 메세지에서 파이어베이스 클라우드 메세지로 바뀐건진 정확히 모르겠지만..



일단 뜬금없이 웹개발하다가 갑자기 회사에서 안드로이드 개발할 일이 생겼지만


웹뷰로 때워버리자! 라고 외친 후 만들어 놓고 보니 안드로이드 알림 메세지가 가야 한단다...



하....


그래서 뭐 삽질좀 하고 여기저기 뒤적뒤적이면서 구현을 했다




일단 가장 먼저


https://console.firebase.google.com/u/0/


당연히 구글 아이디는 있을것이지 콘솔 파이어베이스로 들어가


알람메세지 구현알 어플을 등록해준다.


이곳의 도움을 많이 받았다. 기초 설정은 여기 보고 따라하자


사실 저 블로그만 봐도


어지간하면 구현 가능하다.



진짜 생각보다 좀 간단하다.



1. Android JAVA 



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
import android.content.SharedPreferences;
import android.util.Log;
 
import com.google.firebase.iid.FirebaseInstanceId;
import com.google.firebase.iid.FirebaseInstanceIdService;
 
import java.io.IOException;
 
import okhttp3.FormBody;
import okhttp3.OkHttpClient;
import okhttp3.Request;
import okhttp3.RequestBody;
 
 
public class MyFirebaseInstanceIDService extends FirebaseInstanceIdService {
 
    private static final String TAG = "MyFirebaseIIDService";
 
    /**
     * Called if InstanceID token is updated. This may occur if the security of
     * the previous token had been compromised. Note that this is called when the InstanceID token
     * is initially generated so this is where you would retrieve the token.
     */
    // [START refresh_token]
    @Override
    public void onTokenRefresh() {
        // 설치할때 여기서 토큰을 자동으로 만들어 준다
        String refreshedToken = FirebaseInstanceId.getInstance().getToken();
        Log.d(TAG, "Refreshed token: " + refreshedToken);
 
        // 생성한 토큰을 서버로 날려서 저장하기 위해서 만든거
        sendRegistrationToServer(refreshedToken);
    }
    
    private void sendRegistrationToServer(String token) {
        // Add custom implementation, as needed.
 
        // OKHTTP를 이용해 웹서버로 토큰값을 날려준다. 
        OkHttpClient client = new OkHttpClient();
        RequestBody body = new FormBody.Builder()
                .add("Token", token)
                .build();
 
        //request
        Request request = new Request.Builder()
                .url("토큰 저장할라고 보낼 URL")
                .post(body)
                .build();
 
        try {
            client.newCall(request).execute();
        } catch (IOException e) {
            e.printStackTrace();
        }
 
    }
}
cs




여기서 우리가 잘 봐야 할 건


30번 라인과 


37번라인의 sendRegistrationToServer 메소드다


30번라인에서 토큰을 생성하는데 여기선 어플을 설치할때 한번 생성한다.


그럼 만들어지자마자 


37번라인 메소드로 날려서 HTTP로 전송한다.


그럼 웹서버에서 저 토큰을 받고 저장을 하게 되고


그 해당 토큰으로 메세지를 날려주면 되는 것이다.




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
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.RingtoneManager;
import android.net.Uri;
import android.support.v4.app.NotificationCompat;
 
import com.google.firebase.messaging.RemoteMessage;
 
 
public class MyFirebaseMessagingService extends com.google.firebase.messaging.FirebaseMessagingService {
    private static final String TAG = "FirebaseMsgService";
 
    // [START receive_message]
    @Override
    public void onMessageReceived(RemoteMessage remoteMessage) {
 
        //추가한것
        sendNotification(remoteMessage.getData().get("message"));
    }
 
    private void sendNotification(String messageBody) {
        Intent intent = new Intent(this, MainActivity.class);
        intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);
        PendingIntent pendingIntent = PendingIntent.getActivity(this0 /* Request code */, intent,
                PendingIntent.FLAG_ONE_SHOT);
 
        Uri defaultSoundUri= RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION);
        NotificationCompat.Builder notificationBuilder = new NotificationCompat.Builder(this)
                .setSmallIcon(R.mipmap.ic_launcher)
                .setContentTitle("FCM Push Test"// 이부분은 어플 켜놓은 상태에서 알림 메세지 받으면 저 텍스트로 띄워준다.
                .setContentText(messageBody)
                .setAutoCancel(true)
                .setSound(defaultSoundUri)
                .setContentIntent(pendingIntent);
 
        NotificationManager notificationManager =
                (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
 
        notificationManager.notify(0 /* ID of notification */, notificationBuilder.build());
    }
 
}
 
cs

MyFirebaseMessagingService.java 얘는 알림메세지를 받았을때 처리하는건데

사실 딱히 건들일거 없이 그대로 사용 하면 된다.








일단 저 블로그에서


MyFirebaseInstanceIDService.java 와

MyFirebaseMessagingService.java



이 두개를 구현했다면 




여기로 들어와서 앱단위로 앱선택하고 보내면


모든 앱 설치자들에게 메세지가 날아간다.



하지만 우린 웹서버에서 푸시메세지를 날려주고 싶을 것이다.


알람이라면 뭐 값이 변한다거나 새로운 정보가 등록 되었을때 날려줘야 하니까


그때마다 저기 접속해서 날려주기엔 너무 귀찮으니까 분명히


웹서버에서 인터벌을 돌리든 트리거를 걸든 해서 웹서버에서 날려주고 싶을 것이다


그런데 웹서버에서 날려주는건


잘 안나와있고 복사 붙여넣기 해도 안되는 경우도 있고 하니까


내가 해서 성공한 코드를 보자










2. JAVA Web Server 



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
@RequestMapping(value="mobile/sendFCM")
    public String index(Model model, HttpServletRequest request, HttpSession session, MobileTokenVO vo)throws Exception{
            
            List<MobileTokenVO> tokenList = fcmService.loadFCMInfoList(vo); 
            
                String token = tokenList.get(count).getDEVICE_ID();
                
                final String apiKey = "파이어 베이스의 서버 API키를 여기에 넣는다";
                URL url = new URL("https://fcm.googleapis.com/fcm/send");
                HttpURLConnection conn = (HttpURLConnection) url.openConnection();
                conn.setDoOutput(true);
                conn.setRequestMethod("POST");
                conn.setRequestProperty("Content-Type""application/json");
                conn.setRequestProperty("Authorization""key=" + apiKey);
 
                conn.setDoOutput(true);
                
                String userId =(String) request.getSession().getAttribute("ssUserId");
 
                // 이렇게 보내면 주제를 ALL로 지정해놓은 모든 사람들한테 알림을 날려준다.
                String input = "{\"notification\" : {\"title\" : \"여기다 제목 넣기 \", \"body\" : \"여기다 내용 넣기\"}, \"to\":\"/topics/ALL\"}";
                
                // 이걸로 보내면 특정 토큰을 가지고있는 어플에만 알림을 날려준다  위에 둘중에 한개 골라서 날려주자
                String input = "{\"notification\" : {\"title\" : \" 여기다 제목넣기 \", \"body\" : \"여기다 내용 넣기\"}, \"to\":\" 여기가 받을 사람 토큰  \"}";
 
                OutputStream os = conn.getOutputStream();
                
                // 서버에서 날려서 한글 깨지는 사람은 아래처럼  UTF-8로 인코딩해서 날려주자
                os.write(input.getBytes("UTF-8"));
                os.flush();
                os.close();
 
                int responseCode = conn.getResponseCode();
                System.out.println("\nSending 'POST' request to URL : " + url);
                System.out.println("Post parameters : " + input);
                System.out.println("Response Code : " + responseCode);
                
                BufferedReader in = new BufferedReader(new InputStreamReader(conn.getInputStream()));
                String inputLine;
                StringBuffer response = new StringBuffer();
 
                while ((inputLine = in.readLine()) != null) {
                    response.append(inputLine);
                }
                in.close();
                // print result
                System.out.println(response.toString());
                
 
        return "jsonView";
    }
cs



위의 코드처럼 쓰면 된다.



8번 라인의 API키는 






저 파이어베이스 콘솔에서 빨간동그라미 누르고 프로젝트 설정의 클라우드 메세징으로 가면


서버키가 나와있다 그 서버키를 넣으면 된다. 




그런데 20번 21번 라인의  주제가 ALL인걸 어떻게 지정 하냐고?


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
 protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
 
 
        // 웹뷰 셋팅팅
       mWebView = (WebView)findViewById(webView);
        mWebView.getSettings().setJavaScriptEnabled(true);
 
        mWebView.loadUrl(myUrl+"/mobile/login");
        mWebView.setWebChromeClient(new WebChromeClient());
        mWebView.setWebViewClient(new WebViewClientClass());
 
        mWebView.addJavascriptInterface(new AndroidBridge(), "android");
 
 
        //이렇게 ALL 추가 하면 이 디바이스는 ALL을 구독한다는 얘기가 된다.
        FirebaseMessaging.getInstance().subscribeToTopic("ALL");
 
    }
cs



안드로이드의 MainActivity.java 의 oncreate에다가 저 18번 라인을 추가 해주자


그런데 주제 등록은 즉시 안되고 몇시간 기다리거나 몇일 기다려야 등록 되는 경우도


있다고 하니까 급하게 하진 말자.




그리고 24번 라인에 들어가는 토큰은 MyFirebaseInstanceIDService.java 의 36번 메소드에 날려준 값이다


여기까지가 기초적인 FCM을 이용한 메세지 보내는 방법이다!


다음강좌는


저 토큰아이디를 어떻게 사용자 ID랑 묶어서 저장하고 원하는 사용자에게


보낼 수 있는지 알아보자



2017/07/10 - [Yame Programmer/Android] - [안드로이드] FCM 토큰 저장방법 개별 보내는 방법 SharedPreferences 사용



[리니지M] 접속 장애 서버와의 접속이 끊어졌습니다.




레벨 25까지 키워놨는데 갑자기 팅겨버린다.. 


으...


그나마 계정 연동해놓고 있어서 다행이지 모르고 계정해제하고 막 이런거 클릭하다가


계정 날아갈 뻔 했다..


계정 연동 방법은


2017/06/21 - [Yame Game Life/Lineage M] - [리니지M] 사전 캐릭터 확인 방법


이 포스팅을 참고 하면 된다.



그런데 도시1섭들은 대기자 6천명까지 간다는데;;


일반 1섭들도 대기자 2천명 1천명 있다고 친구들끼리의 단톡방에서 난리다.


그런데 난 서버랑 접속도 못하고 그냥 서버와의 접속이 끊어졌다며 내 캐릭터 확인도 


못하고 있는 실정...


서버 증축이나 뭐 엔지니어들이 열심히 고생 했겠지만..


막상 접속 안되면 짜증은 난다..


사실 나도 새로운 프로그래밍 언어 공부 할땐 게임을 만드는 걸로 익히는 편인데


게임 만드는게 절대 쉬운일이 아니지.. 유저들이 바라는건 겁내 많고....



으.. 이 새벽에 이렇게 주절주절 포스팅 하면서


다시 접속이 되었으면 좋겠다.



-----------------------------------



으... 접속은 되었는데





사람 많은거 싫어서 시골섭인 켄라3섭에서 시작했는데도 이시간에 대기 인원이라니..


6천명 대기 인원보다는 났지만 서도 말이다.


MongoDB 기본 명령어


설치방법은 여러군데 소개되어 있으니 패스.


1. 접속 방법


C:\Program Files\MongoDB\Server\3.2\bin



윈도우에서 몽고DB 설치 경로의 bin 폴더를 환경변수 path에 등록하지 않았다면 


CMD에서 해당 경로로 이동후


만약 path설정 했다면 아무데서나 


1
> mongod
cs



라는 명령어로 몽고DB 서버를 실행 시킨다


그 이후


1
> mongo

cs


명령어로 접속 하면 된다.






2. 데이터베이스 생성



1
> use yamea_db
cs



use db명


이런식으로 DB를 생성해 줄 수 있다


현재 사용중인 DB를 확인 하려면


1
> db
cs



db라는 명령으를 사용하면 현재 사용중인 DB명이 나타난다



내가 만든 DB 리스트를 확인하는 방법은


1
> show dbs
cs


show dbs 라는 명령으를 사용 하면 되는데 데이터베이스를 만들자마자


확인하는 경우엔 목록이 뜨지 않는다


최소한 한개 document를 추가 해야 한다


1
> db.book.insert({"name""Yamea MongoDB""author""cheesu"});
cs


이런식으로 추가 하면


디비목록에 보여지게 된다.




3. 데이터베이스 제거


1
2
3
4
 > use yamea_db
switched to db mongodb_tutorial
> db.dropDatabase();
"dropped" : yamea_db "ok" : }
cs


이렇게 use 명령어로 삭제하려는 db 접속후


db.dropDatabase(); 


명령어를 사용하면 데이터베이스 제거를 할 수 있다.




4. 컬렉션 생성


1) createCollection 생성



use 명령어로 DB 접속 후


1
 > db.createCollection("book")
cs


명령어로 생성



2) createCollection + 옵션 생성





1
 > db.createCollection("articles", {capped: true, size: 6142800,max: 10000)}
cs



이렇게 한줄로 쭉 써도 되고


1
2
3
4
5
6
 > db.createCollection("articles", {
... capped: true,
... autoIndex: true,
... size: 6142800,
... max: 10000
... })
cs



이렇게 db.createCollection("articles",{  까지만 입력후 엔터를 쳐서


옵션을 하나씩 추가하는 방법도 가능하다


그런데 현재 버전에서 autoIndex를 포함시키면 에러가 나는데


이유는 모르겠다. 어차피 처음 배우는거니 크게 신경쓰진 말고 이런게 있구나 하고 알아두기만 하자



3) document추가로 인한 자동 생성


createCollection 명령어를 사용하지 않아도 document를 추가 하면 자동으로 컬렉션이 생성 된다.



이렇게 추가 한 컬렉션을 



1
 > show collections
cs


show collections 명령으를 사용하면 만든 컬렉션 목록을 보여주는데




이렇게 나타나게 된다.





5. 컬렉션 제거


컬렉션 제거는 drop() 메소드를 사용 한다.


1
2
3
4
5
6
7
8
9
10
11
> use test
switched to db test
> show collections
articles
book
people
> db.people.drop()
true
> show collections
articles
book
cs



이렇게 하면 people 컬렉션이 사라짐을 확인 할 수 있다.




6. Document 추가


이 Document가 일반 RDBMS에서 말하는 row, data, 행, 


뭐 이런거라고 생각하면 조금 이해가 빠를 거라 생각 된다.



추가 방법은 


1
> db.book.insert({"name""Yamea Guide""author""cheesu"})
cs



이렇게 하나의 다큐먼트를 추가하는 방법과


여러줄을 넣는 방법이 있는데 배열형식으로 전달해 주면 여러 다큐먼트를 동시에 추가 할 수 있다.


1
2
3
4
> db.book.insert([
... {"name""Book1""author""Cheesu"},
... {"name""Book2""author""Yamea"}
... ]);
cs



이런식으로 가능 하며


컬렉션의 다큐먼트 리스트를 확인 하는 방법은


1
> db.book.find()
cs


db.컬렉션이름.find()  이렇게 사용 하면 된다.



이렇게 넣은 document 들을 확인 할 수 있다.




7. Document 제거


db.컬렉션이름.remove(criteria, justOne)


이런 명령어를 사용 하는데


critetia : 삭제할 데이터의 기준값. 이 값이 {} 이면 컬렉션의 모든 데이터를 제거

justOne : 선택적 매개변수이며 이 값이 true면 1개의 다큐먼트만 제거 합니다. 

           생략하면 기본값은 false이며 criteria에 해당되는 모든 다큐먼트들을 제거 한다.



위의 document에서


name이 "yamea Book1"인 Document를 제거 하기 위해선

1
> db.book.remove({"name""Yamea Book1"})
cs



이런 명령어를 사용 하면 된다.





Yame Book1이 사라진걸 확인 할 수 있다.






Cannot find module 'ejs' 에러 해결 방법


드디어 node.js 에 나도 입문을 하게 되었다.


node.js 설치 과정은 다른 블로그에도 많이 나와있고 나중에 시간이 되면 포스팅 하기로 하고


이 포스팅 내용을 검색해 들어온 사람들은 이미 설치를 끝내고 나서 진행하다가 찾아 온 사람들이니 생략


그리고 진짜 오늘 시작해본것 이기 때문에 진정한 야매가이드라는걸 잊지 말고


내 포스팅을 전부 신뢰하진 말길....




nodejs 서버에 jsp 같은 파일을 띄우려면 템플릿? 이라는걸 사용해야 한단다


jade도 있고 ejs도 있는데 html이랑 제일 유사하게 생긴게 ejs라고 하길래 어떻게 하는가 찾아 봤더니


ejs를 설치 하란다


그래서 설치 하고 예제에 있는대로 작성하고 서버를 실행시켰더니


Cannot find module 'ejs' 라는 에러가 나타난다





이런게 뜨길래 구글링해서 찾아봤더니


역시나 스택오버플로우 형님들이 해결책들을 제시해 주었다




package.json 이라는 녀석을 열어 보면 저렇게 mvc2에서 디펜던시 해주는것처럼 종속성에 대한 정보가 들어있는 녀석이 있는데


저기에 아마 ejs 라는 동그라미친 부분이 없어서 나오는 에러이다


저부분을 추가해 주면 해결이 된다고 하는데 난 해결이 안된다...



그래서 스크롤을 좀더 내려보니


npm install ejs --save


이렇게 설치를 해야 한다고 한다


npm 명령어중 --save 는


설치된 node 모듈을 pakage.json 파일 내의 디펜던시 목록에 추가 하게 하는 옵션이다.


나도 아직 nodejs 설치 한지 두시간밖에 안지나서 정확히는 모르겠다.



여하튼 에러나는 사람은 저렇게 다시 설치 해보도록 하자



그리고 만약 프로젝트 폴더에 package.json 이 파일이 없다면


프로젝트 등록? 뭐 그게 안된거다


방법은 해당 프로젝트 폴더로 들어가서


npm init 라는 명령어를 실행시켜


어플리케이션을 보관할 디렉토리를 작업 디렉토리로 설정 하도록 하자


대충 엔터치면 지나가는데


해당 어플리케이션 설명이나 리파지토리 뭐 이런거 적으라고 나오는건


알아서 적도록 하자 그냥 엔터쳐서 넘겨도 된다.


그중에


entry point:(index.js) 라고 나오는데


기본 파일의 이름을 지정 하는것 이라고 한다.



ps: 뒤에 --save 붙이는게 의무적인건 아니라고 한다... 근데 난 저거 붙여서


해결했으니까 ㅠ_ㅠ;;; 


ps2: 에러난 이유가 환경 변수의 문제일 수도 있겠다는 제보를 받았다

노드js는 C에 설치 하고 프로젝트폴더는 D에 있어서 

내가 처음에 설치 할떈 글로벌로 설치 하면 드라이브 상관 없이

다 되는 건줄 알고 npm install ejs -g 라는 -g 옵션을 썻는데

이렇게 설치 하면 C에 경로가 잡히가 되는거 아니냐는 의견을 받았음.

확인 해보진 않았지만 혹시나 다른 사람들은 확인해보시길







회사에서 처음 노트북을 지급받고 이클립스 자바 등등등을 설치 한 후 실행을 해보면

404에러가 뜨는 경우가 많다. 경로를 찾지 못해서 나타나는 에러인데 

에러가 나는 부분도 없는데 에러가 나는 경우가 있다. 이런 경우 서버를 체크해보자!


보통 이런 경우는

서버에 루트 지정이 잘못된 경우이다.


해당서버 컨피그 폴더안에 있는


server.xml 파일 Context 태그 안에 path설정을 해주어야 한다. 


아마 path에 /프로젝트이름 

으로 되어 있거나 다른 이름들이 들어가 있는 경우일텐데

보통 그냥 "/" 이걸로 바꿔주면 실행이 될 것이다. 도메인 뒤에 다시 프로젝트이름이 나타나는건 보기 안좋기 때문에

그냥 저렇게 하는 듯 하다.



포스팅이 도움이 되셨다면 마우스를 조금만 움직여서 

공감하기를 눌러주세요 블로거에게 큰 힘이 됩니다~! 


+ Recent posts