com.sun.net.httpserver.HttpServer 을 이용해 java에서 간단한 웹서버를 만들었습니다.

만든 서버는 GET 방식을 이용해서 Request 받고 

결과를 처리해 줍니다. 


GET 방식으로 http의 주소에 붙어오는 key와 value를 JAVA 의 MAP에 매핑하고 싶었습니다.


그 방법을 메모해 둡니다.



public Map<String, String> queryToMap(String query)
{
    Map<String, String> result = new HashMap<String, String>();
    for (String param : query.split("&")) {
        String pair[] = param.split("=");
        if (pair.length>1) {
            result.put(pair[0], pair[1]);
        }else{
            result.put(pair[0]"");
        }
    }
    return result;
}



위의 함수가 GET 방식의 쿼리를 JAVA Map에 매핑후 리턴해 줍니다.


사용은 아래와 같이 하시면 됩니다.


Map<String, String> params = queryToMap(HttpExchange exchange.getRequestURI().getQuery())
System.out.println("param A=" + params.get("A"));




키가 A 인 값을 MAP 에서 찾아 출력해 줍니다.






배경 이미지를 용량 때문에 작게 만들어서 크게 늘려서 쓰고 싶었습니다. 



bg3 = new CCSprite().sprite("img/bk0.png");

bg3.setAnchorPoint(ccp_a(0, 0));

bg3.setPosition(ccp_p(0, 0));


CGSize tTargetSize = CGSize.make( GAME_WIDTH, GAME_HEIGHT);

CGSize tSizeOrig = bg3.getContentSize();

bg3.setScaleX(( tTargetSize.width / tSizeOrig.width ));

bg3.setScaleY(( tTargetSize.height / tSizeOrig.height ));

addChild(bg3);



빨간 부분을 추가 하시면 됩니다. 


작은 크기의 이미지를 Game Width, Game Height 크기 만큼 늘렸습니다. 


별다른 무늬가 없는 하늘 배경이라 위와같이 늘려서 썼습니다. 






SimpleDateFormat 패턴 정리

기호의미보기
G연대(BC,AD)AD
y년도2012
M월(1~12월)7
w년의 몇 번째 주(1~53)28
W월의 몇 번째 주(1~5)2
D년의 몇 번째 일(1~366)194
d월의 몇 번째 일(1~31)11
F월의 몇 번째 요일(1~5)3
E요일
a오전/오후(AM,PM)PM
H시간(0~23)0
k시간(1~24)24
K시간(0~11)10
h시간(1~12)10
m분(0~59)22
s초(0~59)7
S천분의 1초(0~999)253
zTime zone(General Time Zone)GMT+9:00
ZTime zone(RFC 822 time zone)+0900





yyyy-MM-dd HH:mm:ss 와 같은 패턴을 위의 표를 참조하시면 쉽게 포멧을 만드실수 있습니다. 






구글은 자사의 서비스를 여러방면으로 사용할수 있게 수많은 API를 제공합니다. 

이번에 게임을 제작하면서 각종 게임 벨런싱 문서들을 구글 스프레드시트에 올려두고 

기획자에게 스프레드시트의 권한을 제공한 다음 직접 문서를 수정하게 했습니다. 


다음은 JAVA 서버에서 기획자가 수정한 스프레드시트의 벨런싱 문서를 가져와서 서버에 적용하며, 

외국 사이트등을 참고해서 만든 구글 API를 이용한 스프레드시트 내용 긁어 오기 demo 입니다. 



우선 구글 api를 사용하기 위해서 구글 라이브러리를 다운 받아야 합니다. 

구글 라이브러리 다운[바로가기]




위와 같이 구글은 많은 언어의 라이브러리를 제공합니다. 


저희는 java 서버이니 java 라이브러리를 다운 받겠습니다. 




자바 라이브러리는 겁나 많습니다. 

총 43개나 되네요. 


근데 어느 사이트도 어떤 라이브러리를 추가해야 한다고 콕 찝어 주지 않았습니다. 

당연히 다 쓰진 않겠기에 필요해보이는 한두개를 추가하고 에러가나면 또 추가하고 하는 식을 몇개 추렸습니다. 


gdata-client-1.0.jar

gdata-client-meta-1.0.jar

gdata-core-1.0.jar

gdata-spreadsheet-3.0.jar

gdata-spreadsheet-meta-3.0.jar

일단 필요해 보이는 애들을 추가해가며 넣은거라 위에서 필요 없는 아이들도 있을수 있습니다.

우선은 이렇게 추가하면 컴파일 에러가 하나 발생합니다. 


이리 저리 찾아보니 또다른 라이브러리를 다운 받아야 하더군요

그녀석이 아래녀석입니다. 

guava-18.0.jar


위에 라이브러리를 추가하니 에러가 발생하지 않습니다. 


우선 사용자 인증을 해야합니다.

SpreadsheetService service = new SpreadsheetService("Gdata Test");
            service.setUserCredentials("계정 E-Mail""계정비번");

위에서 "Gdata Test"의 명은 아무것이나 입력해도 됩니다. 



그리고 내가 접근 가능한 계정의 스프레드시트 Full feed를 읽어 옵니다. 

URL metafeedUrl = new       URL("https://spreadsheets.google.com/feeds/spreadsheets/private/full");
            SpreadsheetFeed feed = service.getFeed(metafeedUrl, SpreadsheetFeed.class);
            List<SpreadsheetEntry> spreadsheets = feed.getEntries();


위에서 많이 헤멨습니다. 

주소 때문에요. 

당연히 스프레드시트의 주소를 넣어야 한다고 생각하여 스프레드 시트 주소를 넣고 돌렸지만 에러밖에 나지 않습니다. 

꼭 "https://spreadsheets.google.com/feeds/spreadsheets/private/full" 이주소를 넣어 주셔야합니다.


해당 주소를 직접 들어가보시면 아시겠지만, 내가 접근 가능한 스프레드시트의 full feed를 보여주는 주소입니다. 

feed를 받은 다음 그 feed를 파싱해가며 접근하는 구조인가 봅니다. 


다음은 데모코드 입니다. 



 import com.google.gdata.client.spreadsheet.*;

import java.net.URL;
import com.google.gdata.data.spreadsheet.*;
import java.util.*;

public class GdataTest {

    /**
     * @param args
     */

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        
        try{
            SpreadsheetService service = new SpreadsheetService("Gdata Test");
            service.setUserCredentials("""");
            
            URL metafeedUrl = new       URL("https://spreadsheets.google.com/feeds/spreadsheets/private/full");
            SpreadsheetFeed feed = service.getFeed(metafeedUrl, SpreadsheetFeed.class);
            List<SpreadsheetEntry> spreadsheets = feed.getEntries();
            
            for(int i=0; i< spreadsheets.size(); i++) 
            {
                SpreadsheetEntry entry = spreadsheets.get(i);
                if(entry.getTitle().getPlainText().equals("스프레드시트 파일 이름")) // 파일 타이틀
                {
                     List<WorksheetEntry> worksheets = entry.getWorksheets()// 시트 타이틀
                    for(int k=0;k< worksheets.size();k++)
                    {
                         WorksheetEntry worksheet = worksheets.get(k);
                         System.out.println(worksheet.getTitle().getPlainText());
                         
                         URL cellFeedUrl = worksheet.getCellFeedUrl();
                         CellFeed cellFeed = service.getFeed(cellFeedUrl, CellFeed.class);
                         for (CellEntry cell : cellFeed.getEntries()) 
                         {
                             System.out.println("getPlainText : "+cell.getTitle().getPlainText())// value의 위치
                             System.out.println("getValue : "+cell.getCell().getValue())// value의 내용
                         }
                    }
                }
            }
            
        }catch(Exception e){
            e.printStackTrace();
        }
    }

}

데모코드를 돌려보면, // 파일 타이틀 부분

"스프레드시트 파일 이름" 이라는 문서를 만나면

// 시트 타이틀 부분 에서 시트의 개수만큼 for 문을 수행하며, 그 안에 내용들을 긁어와서 화면에 찍어줍니다. 


System.out.println("getPlainText : "+cell.getTitle().getPlainText())// value의 위치

System.out.println("getValue : "+cell.getCell().getValue())// value의 내용


두부분이 실제적으로 안의 내용을 찍어주는 역활을 합니다. 

getPlainText : A22

getValue : store_list

getPlainText : B22

getValue : store_price


결과는 위와 같이 찍힙니다. 


A22셀의 내용은 store_list

B22셀의 내용은 store_price 


위와같이 22번 셀을 기준으로 A -> B 순으로 오른쪽으로 순차적으로 읽어 오네요. 









개발후 결론 

생각보다 무지 느려요. '-' 못써요. 

진짜 벨런싱 수정용으로 밖에 못쓸거 같아요. 

게임 서비스 하면서도 각종 설정파일들을 읽어와서 서버에 실시간 적용을 위해서 

구글의 막강한 스프레드시트를 이용해서 개발한건데 ㅠ.ㅠ 너무 너무 느려서 서비스중인 게임엔 적용이 힘들것 같구요. 


기획자들의 편의를 위해 사용하자는 기존의 목적대로 밖에 못쓸것 같습니다. 

기획자들은 적용이 완료될때까지 기다려 줄테니까요 ^ㅡ^


결론2

기획자들의 편의를 위해 제공한다면 굳이 구글 스프레드시트가 아닌 엑셀파일이나 더 쉽게는 csv 파일을 제공하여, 

그 파일에서 직접 읽어오는게 속도는 훨씬 빠릅니다. 


결론3

그래도 구글 스프레드시트를 이용하면 웹이접근되는 모든 공간에서 수정과 실시간 반영을 해줄수 있으니, 

또 나름 괜찮아요 ^ㅡ^





  1. G.J. 제이 2015.03.07 16:56 신고

    개발자분들에게 유용한 정보인가요?
    그런데 적샷굿샷님 태그 위의 광고인가. 웹 페이지를 찾을 수 없다고 예전부터 계속 뜨네요!

    • 적샷굿샷 2015.03.09 09:03 신고

      헙~ 저는 빈공간으로 표시되서 그냥 두고 있었는데 ^^

      다음뷰 입니다.
      서비스 종료해서 이제 안나와요 ㅋㅋㅋ

      스킨 수정해야겠네요.
      알려 주셔서 감사드립니다.

  2. 준호 2016.01.13 10:08 신고

    이 소스 안드로이드 스튜디오 에서도 활용가능한가요?? 구글스프레드시트 기능을 사용해서

    필요한정보를 사용자에게 전달해주는 방식으로 만들어보고싶은데

    • 적샷굿샷 2016.01.17 21:21 신고

      실제로 저희팀 클라이언트분이
      안드로이드에서도 사용을 했었습니다.

      속도만 빠르면 별도의 서버운영 없이
      간단한 게임의 유저DB 로도 충분히 사용할수 있을텐데

      속도가 너무 느려서 포기했어요 ^^

  3. 만두집아들 2017.12.24 12:02 신고

    안녕하세요. 제가 방송을 하는 사람입니다.
    방송채팅에서 누군가가 무엇을 치면 그에 반응해서 스프레드시트가 바로 수정되는걸 하고싶은데
    어떤분이 스크립트를 이용하면될거같다고해서 찾다보니 이글을 찾았는데 혹시 그런걸 하려면 어떤기능등을 이용해야할까요...
    ㅠ.ㅠ

    • 적샷굿샷 2017.12.24 12:16 신고

      채팅창을 주기적으로 읽어 오는 별도의 기능이 구현되면 다음에 이기능을 이용해서 스프레드 시트에 값을 추가 변경할수 있을것 같아요

요즘 자바 GUI를 이용해서 게임 서버 관리툴을 만들고 있습니다. 

요즘은 관리툴을 모두 웹으로 만들고 있는 추세이지만, 일정이 촉박하여 아무래도 손에 제일 익은 자바로 

후딱 만들고 있어요 ^^


자바 스윙을 이용해서 메인 Frame을 생성하면 화면의 좌측 상단에서 부터 창이 생깁니다. 


멋없죠 '- ' ;;;



화면 가운데 띄우고 싶어서 이것 저것 찾아보고 메모차원에서 남겨둡니다. 


  1. // 창 화면 중간에 띄우기
  2. // 프레임의 사이즈를 구합니다.
  3. Dimension frameSize = this.getSize();
  4. // 내 모니터의 크기를 구합니다.
  5. Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
  6. /*
  7. * 그래서 프레임의 위치를
  8. * (모니터화면 가로 - 프레임화면 가로) / 2,
  9. * (모니터화면 세로 - 프레임화면 세로) / 2 이렇게 설정한다.
  10. */
  11. this.setLocation((screenSize.width - frameSize.width)/2(screenSize.height - frameSize.height)/2);
  12.        
  13. setResizable(false)// 프레임 크기 조절을 할수 없게 만듭니다.
  14. setVisible(true);    // 프레임을 보여줍니다.







이렇게 하면 끝 ^ㅡ^ 


참 쉽죠?






리눅스 명령어중 해당 파일의 경로를 알려주는 명령어는 which 입니다. 


jdk 설치후 java의 경로를 알고 싶을때는 which 명령어를 사용하시면 됩니다.


ex) which java 

혹시 나오지 않는다면 find를 사용하시면 전체를 다 뒤져줍니다.


최상위 경로에서 

ex) find -name java

대신 시간이 좀 오래 걸립니다.


참 쉽죠?


해당 명령어는 응용이 무궁 무진합니다.


ex) which mysql


등등 으로도 쓸수 있습니다.





해당 소스는 다운받은 Netty의 예제 폴더에 있습니다.


폴더 경로 : netty-3.6.6.Final\src\main\java\org\jboss\netty\example\echo



EchoServer.java

  

1 public class EchoServer {

  2  
  3     private final int port;
  4  
  5     public EchoServer(int port) {
  6         this.port = port; 
  7     } 
  8  
  9     public void run() {
 10         // Configure the server. 
 11         ServerBootstrap bootstrap = new ServerBootstrap(
 12                 new NioServerSocketChannelFactory(
 13                         Executors.newCachedThreadPool(), 
 14                         Executors.newCachedThreadPool())); 
 15  
 16         // Set up the pipeline factory. 
 17         bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
 18             public ChannelPipeline getPipeline() throws Exception {
 19                 return Channels.pipeline(new EchoServerHandler());
 20             } 
 21         }); 
 22  
 23         // Bind and start to accept incoming connections. 
 24         bootstrap.bind(new InetSocketAddress(port));
 25     } 
 26  
 27     public static void main(String[] args) throws Exception {
 28         int port; 
 29         if (args.length > 0) {
 30             port = Integer.parseInt(args[0]);
 31         } else { 
 32             port = 8080; 
 33         } 
 34         new EchoServer(port).run(); 
 35     } 
 36 }

EchoServerHandler.java


1 public class EchoServerHandler extends SimpleChannelUpstreamHandler {

  2  
  3     private static final Logger logger = Logger.getLogger(
  4             EchoServerHandler.class.getName()); 
  5  
  6     private final AtomicLong transferredBytes = new AtomicLong();
  7  
  8     public long getTransferredBytes() {
  9         return transferredBytes.get(); 
 10     } 
 11  
 12     @Override 
 13     public void messageReceived(
 14             ChannelHandlerContext ctx, MessageEvent e) { 
 15         // Send back the received message to the remote peer. 
 16         transferredBytes.addAndGet(((ChannelBuffer) e.getMessage()).readableBytes());
 17         e.getChannel().write(e.getMessage()); 
 18     } 
 19  
 20     @Override 
 21     public void exceptionCaught(
 22             ChannelHandlerContext ctx, ExceptionEvent e) { 
 23         // Close the connection when an exception is raised. 
 24         logger.log( 
 25                 Level.WARNING, 
 26                 "Unexpected exception from downstream.", 
 27                 e.getCause()); 
 28         e.getChannel().close(); 
 29     } 
 30 }






'Netty' 카테고리의 다른 글

[java][netty] netty를 이용한 echo server 예제  (0) 2013.05.27
[java][netty] Netty Korean User Group 주소  (0) 2013.05.27

자바를 이용한 서버를 만들어 사용해오다가 이희승님이 개발하신 오픈소스 자바 프레임워크 Netty를 알게됐습니다. 


한국인이 개발한 라이브러리지만 생각보다 한글 자료는 얼마 없네요. 


다행인 것은 개발자님이 직접 댓글을 달아주시는 Netty Korean User Group(바로가기) 이 있습니다. 


Netty를 처음 시작하시는 분들은 즐겨찾기에 등록해 두셔도 좋을것 같네요.






'Netty' 카테고리의 다른 글

[java][netty] netty를 이용한 echo server 예제  (0) 2013.05.27
[java][netty] Netty Korean User Group 주소  (0) 2013.05.27

지난번엔 자바의 Timer class를 이용한 타이머를 포스팅 했었는데요.

Timer class를 이용한 타이머 만들기 바로가기 : http://ince.co.kr/261


이번엔 ScheduledExecutorService Class 를 이용해 타이머 만들기를 포스팅해보겠습니다. 



//타이머 시작

  1 ScheduledExecutorService testTimer = Executors.newSingleThreadScheduledExecutor();
  2 testTimer.scheduleAtFixedRate(new TestTask(), 0, 1, TimeUnit.SECONDS); // 1초마다 체크    

main() 함수 같은곳에 위와같이 적으면 타이머가 구동됩니다. 


아래는 실제로 타이머가 구동되는 Thread 입니다.


  1 class TestTask implements Runnable { // 1초마다 체크 
  2     public void run() {
  3                 System.out.println("타이머 달려 달려!!!!"); 
  4         } 
  5 };



초 주기를 변경하고 싶으면 선언부에서 


testTimer.scheduleAtFixedRate(new TestTask(), 0, 30, TimeUnit.SECONDS); // 30초마다 체크


과 같이 변경해 주면 간단하게 됩니다.



참 쉽죠?







  1. 잉여토기 2013.04.19 00:39 신고

    와, 이렇게 하면 타이머를 직접 만들 수 있는 건가요?
    신기하네요.



안드로이드 앱을 개발하는 궁극적인 목적!


그것은 바로 수익 창출이 아닐까 합니다. 


현재 많은 개발자들은 광고를 통해서 수익을 창출 하지만 


인앱을 통해서 수익을 창출 하려면 구글이 제공하는 인앱 소스를 사용해야만 합니다. 


onPurchaseStateChange() 함수를 통해서 정상 결재 여부를 판단하게 되는데요


이놈이 호출이 되지 않거나 간혈적으로 호출되는 문제가 있습니다. 


주로 여러번 결재 시도를 하다보면 자주 발생하게 됩니다. 


http://stackoverflow.com 에서 해결책을 찾았습니다. 



출처 : http://stackoverflow.com/questions/12844132/onpurchasestatechange-not-getting-called


onstart() 에서 호출된 녀석을 re-start 할때 제대로 remove를 하지 않아서 발생한다는 내용입니다. 

그래서 해결책은 



protected void onStop() {
        super.onStop();

        ResponseHandler.unregister(YOUR_PURCHASE_OBSERVER);
    }


onStop() 안에서 unregister 을 호출해서 문제를 해결하면 됩니다. 



저희 앱에서는 pause 안에 해당 unregister 가 존재했지만 같은 현상이 계속 나타났습니다. 

이놈을 onDestroy로 옮기니 문제가 해결 되었습니다. 










아래 손가락을 꾹 눌러 주세요

  1. 초짜개발자 2013.01.03 17:18 신고

    저도 같은 현상때문에 여기까지 왔네요.
    링크글을 읽어보니 onstop 에서의 내용을
    onDestroy 로 옮기라는 내용아닌가요? ;;
    onstop 에서 옵저버를 null 로 만드니까요.
    null 일 때 queue 로 처리하라는 두번째 해결책은
    제가 개발력이 부족해서 어떻게 해야되는지 모르겠네요 ㅎ




오랜만에 블로그 포스팅을 하게 되네요 ^^
그간 이직 하고 하고 싶던 프로그래밍 원없이 했습니다.

이제좀 프로그래머로서의 삶을 다시 사는 느낌이네요.

이번 프로젝트 진행중에 발생한 문제를 해결하며
메모차원에서 포스팅을 남겨둡니다.

한글을 전송함에 있어서 여러 가지 문제점들이 많이 발생하는것은 모두들 아실텐데요.
웹게임을 만들면서

DB -> java 데몬 -> javaScript 로 데이터를 전송하며 모두 UTF-8 로 처리했지만
어김없이 한글 깨짐 현상이 발생했습니다.

아마도 DB->java 데몬 으로 옮겨 오면서 UTF-8 처리가 제대로 되지 않은걸로 추측됩니다.



이문제에 대해서 해결한 방법을 아래에 적었습니다.
 java
 URLEncoder.encode("한글","UTF-8")
 URLDecoder.decode("한글","UTF-8")


java script
 function urldecode (str) { 
    return decodeURIComponent((str + '').replace(/\+/g, '%20'));  // 공백 문자인 + 를 처리하기 위해 +('%20') 을 공백으로 치환
  }
위와 같이  URL 엔코더와 디코더를 이용해서 문제를 해결했습니다.




URL 엔코더의 경우 주로 검색창에 한글로 검색했을 경우
검색된 페이지의 url을 보시면 "검색+한+글자" 와 같은 것을들 자주 볼수 있었을 텐데요.

URL로 엔코딩후 다시 디코딩을 하면 공백에 + 기호로 처리되어 위의
java Script 에서도 + 기호가 모두 들어간채로 디코딩 되었습니다.

그래서 위와 같은 함수를 만들었습니다.
핵심은 decodeURIComponent((str + '').replace(/\+/g, '%20'));  // 공백 문자인 + 를 처리하기 위해 +('%20') 을 공백으로 치환
이부분 입니다.

+를 만나면 그냥 공백으로 치환해 버리는거죠 '%20' <-- 요놈이 + 기호 입니다.

좀 불편하긴 하지만 한글이 깨지지 않고 다른 언어들 간에 전송을 할수 있는 방법으로는
이방법이 제일 안전하고 괜찮아 보입니다.


저도 인터넷 뒤져가며 배우고 만든거라 다시 인터넷으로 돌려드릴겸
그리고 다음에 또 사용할때를 대비해서 메모도 해둘겸 포스팅을 남깁니다.

모두들 유용하게 이용하시길 ^^








  1. gsbob 2012.06.27 14:46 신고

    감사합니다.
    문제가 말끔히 해결됐네요 ㅎㅎ

    이 내용 참고해서.. 블로그에 포스팅좀 해도 되죠??^^

    • 적샷굿샷 2012.06.28 15:20 신고

      해결되셨다니 다행이네요

      출처만 밝혀주신다면
      어디든 퍼가셔도 좋아요 ^^

+ Recent posts