728x90

PHP로 만들어진 달력을 활용해보고자 많은 자료를 검색해봤지만 결국에는 기본적인 개념이 충실한 PHP 달력 만들기 소스코드를 좀 더 정교하게 가다듬어 정리를 하는 것이 답인 거 같다.


달력은 일정관리, 음력일정, 음력 기념일 등을 관리할 수 있어야 하므로 외국 소스를 활용하는 것은 큰 도움이 안되더라.


bootstrap 기반으로 포팅하고 약간 수정해서 정리한 걸 적어둔다.


양력기반으로 일정추가가 잘된 소스는 https://phppot.com/php/php-calendar-event-management-using-fullcalendar-javascript-library/ 를 참조하면 도움이 될 것이다.

그리고 https://www.phpzag.com/create-event-calendar-with-jquery-php-and-mysql/ 자료도 도움이 된다.



음력 기념일, 일정 등이 포함된 버전으로 만들기 위해서는 좀 더 정리가 필요하다.

음력 일정까지 정리되는 달력은 다른 소스를 가지고 수정 작업을 하고 있는 중이다.


<?php
//---- 오늘 날짜
$thisyear = date('Y'); // 4자리 연도
$thismonth = date('n'); // 0을 포함하지 않는 월
$today = date('j'); // 0을 포함하지 않는 일

//------ $year, $month 값이 없으면 현재 날짜
$year = isset($_GET['year']) ? $_GET['year'] : $thisyear;
$month = isset($_GET['month']) ? $_GET['month'] : $thismonth;
$day = isset($_GET['day']) ? $_GET['day'] : $today;

$prev_month = $month - 1;
$next_month = $month + 1;
$prev_year = $next_year = $year;
if ($month == 1) {
    $prev_month = 12;
    $prev_year = $year - 1;
} else if ($month == 12) {
    $next_month = 1;
    $next_year = $year + 1;
}
$preyear = $year - 1;
$nextyear = $year + 1;

$predate = date("Y-m-d", mktime(0, 0, 0, $month - 1, 1, $year));
$nextdate = date("Y-m-d", mktime(0, 0, 0, $month + 1, 1, $year));

// 1. 총일수 구하기
$max_day = date('t', mktime(0, 0, 0, $month, 1, $year)); // 해당월의 마지막 날짜
//echo '총요일수'.$max_day.'<br />';

// 2. 시작요일 구하기
$start_week = date("w", mktime(0, 0, 0, $month, 1, $year)); // 일요일 0, 토요일 6

// 3. 총 몇 주인지 구하기
$total_week = ceil(($max_day + $start_week) / 7);

// 4. 마지막 요일 구하기
$last_week = date('w', mktime(0, 0, 0, $month, $max_day, $year));
?>

<!DOCTYPE html>
<html lang="en">
<head>
  <title>Bootstrap Example</title>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <link rel="stylesheet" href="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
  <style>
    font.holy {font-family: tahoma;font-size: 20px;color: #FF6C21;}
    font.blue {font-family: tahoma;font-size: 20px;color: #0000FF;}
    font.black {font-family: tahoma;font-size: 20px;color: #000000;}
  </style>
  <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.12.2/jquery.min.js"></script>
  <script src="http://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/js/bootstrap.min.js"></script>
</head>
<body>
<div class="container">
<table class="table table-bordered table-responsive">
  <tr align="center" >
    <td>
        <a href=<?php echo 'index.php?year='.$preyear.'&month='.$month . '&day=1'; ?>>◀◀</a>
    </td>
    <td>
        <a href=<?php echo 'index.php?year='.$prev_year.'&month='.$prev_month . '&day=1'; ?>>◀</a>
    </td>
    <td height="50" bgcolor="#FFFFFF" colspan="3">
        <a href=<?php echo 'index.php?year=' . $thisyear . '&month=' . $thismonth . '&day=1'; ?>>
        <?php echo "&nbsp;&nbsp;" . $year . '년 ' . $month . '월 ' . "&nbsp;&nbsp;"; ?></a>
    </td>
    <td>
        <a href=<?php echo 'index.php?year='.$next_year.'&month='.$next_month.'&day=1'; ?>>▶</a>
    </td>
    <td>
        <a href=<?php echo 'index.php?year='.$nextyear.'&month='.$month.'&day=1'; ?>>▶▶</a>
    </td>
  </tr>
  <tr class="info">
    <th hight="30">일</td>
    <th>월</th>
    <th>화</th>
    <th>수</th>
    <th>목</th>
    <th>금</th>
    <th>토</th>
  </tr>

  <?php
    // 5. 화면에 표시할 화면의 초기값을 1로 설정
    $day=1;

    // 6. 총 주 수에 맞춰서 세로줄 만들기
    for($i=1; $i <= $total_week; $i++){?>
  <tr>
    <?php
    // 7. 총 가로칸 만들기
    for ($j = 0; $j < 7; $j++) {
        // 8. 첫번째 주이고 시작요일보다 $j가 작거나 마지막주이고 $j가 마지막 요일보다 크면 표시하지 않음
        echo '<td height="50" valign="top">';
        if (!(($i == 1 && $j < $start_week) || ($i == $total_week && $j > $last_week))) {

            if ($j == 0) {
                // 9. $j가 0이면 일요일이므로 빨간색
                $style = "holy";
            } else if ($j == 6) {
                // 10. $j가 0이면 토요일이므로 파란색
                $style = "blue";
            } else {
                // 11. 그외는 평일이므로 검정색
                $style = "black";
            }

            // 12. 오늘 날짜면 굵은 글씨
            if ($year == $thisyear && $month == $thismonth && $day == date("j")) {
                // 13. 날짜 출력
                echo '<font class='.$style.'>';
                echo $day;
                echo '</font>';
            } else {
                echo '<font class='.$style.'>';
                echo $day;
                echo '</font>';
            }
            // 14. 날짜 증가
            $day++;
        }
        echo '</td>';
    }
 ?>
  </tr>
  <?php } ?>
</table>
</div>

</body>
</html>


음력을 지원하는 달력의 모습


음력 달력에 대한 소스 코드 설명은 http://link2me.tistory.com/1545 를 참조하면 된다.

'Web 프로그램 > PHP 응용 및 활용' 카테고리의 다른 글

PHP 달력 소스 코드 (음력, 간지, 기념일, 일정등록)  (9) 2018.11.05
법정공휴일  (0) 2018.11.03
날짜 선택  (0) 2018.10.26
접속단말 및 IP주소  (0) 2018.06.11
PHP 글자수 줄임 표시  (0) 2018.06.09
블로그 이미지

Link2Me

,
728x90

PHP에서 날짜 선택을 깔끔하게 하는 코드를 적어둔다.


<?php
//---- 오늘 날짜
$thisyear = date("Y");
$thismonth = date("m");
$today = date("d"); // 1, 2, 3, ..., 31

//------ $year, $month 값이 없으면 현재 날짜
$year = isset($_GET['year']) ? $_GET['year'] : $thisyear;
$month = isset($_GET['month']) ? $_GET['month'] : $thismonth;
$day = isset($_GET['day']) ? $_GET['day'] : $today;

$start_date = date("Y", mktime(0, 0, 0, date("m"), date("d"), date("Y") - 1));
$end_date = date("Y", mktime(0, 0, 0, date("m"), date("d"), date("Y") + 3));
?>


<TR>
    <TD >시작날짜</TD>
    <TD colspan="2" rowspan="1">
    <select name="start_date1">
    <?php for($i=$start_date;$i<$end_date;$i++):?><option value="<?php echo $i?>"<?php if($year==$i):?> selected="selected"<?php endif?>><?php echo $i?>년</option><?php endfor?>
    </select>
    <select name="start_date2" >
    <?php for($i=1;$i<13;$i++):?><option value="<?php echo sprintf('%02d',$i)?>"<?php if($month==$i):?> selected="selected"<?php endif?>><?php echo sprintf('%02d',$i)?>월</option><?php endfor?>
    </select>
    <select name="start_date3" >
    <?php for($i=1;$i<32;$i++):?><option value="<?php echo sprintf('%02d',$i)?>"<?php if($day==$i):?> selected="selected"<?php endif?>><?php echo sprintf('%02d',$i)?>일</option><?php endfor?>
    </select>

    </TD>
</TR>

'Web 프로그램 > PHP 응용 및 활용' 카테고리의 다른 글

법정공휴일  (0) 2018.11.03
PHP 달력 만들기 소스  (0) 2018.10.30
접속단말 및 IP주소  (0) 2018.06.11
PHP 글자수 줄임 표시  (0) 2018.06.09
PHP to JSP  (0) 2018.04.28
블로그 이미지

Link2Me

,
728x90

구글 캘린더를 연동하기 위한 API 사용 설정 과정이다.







7번 항목은 http://link2me.tistory.com/1406 참조하면 구할 수 있다.





10번 항목을 구글과 관련된 명칭으로 하면 에러가 나면서 Client ID가 생성되지 않는다는 걸 확인했다.



아래 그림은 개발용 PC/노트북이 다른 환경(집, 사무실)에서 등록하는 방법은 추가를 하는 것이다.








AndroidManifest.xml


<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.tistory.android.googlecalendarapi">

    <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
    <uses-permission android:name="android.permission.INTERNET" />
    <uses-permission android:name="android.permission.GET_ACCOUNTS" />

    <application
        android:allowBackup="true"
        android:icon="@mipmap/ic_launcher"
        android:label="@string/app_name"
        android:roundIcon="@mipmap/ic_launcher_round"
        android:supportsRtl="true"
        android:theme="@style/AppTheme">
        <activity android:name=".MainActivity">
            <intent-filter>
                <action android:name="android.intent.action.MAIN" />

                <category android:name="android.intent.category.LAUNCHER" />
            </intent-filter>
        </activity>

        <meta-data
            android:name="com.google.android.gms.version"
            android:value="@integer/google_play_services_version" />

    </application>

</manifest>


블로그 이미지

Link2Me

,
728x90

Node.js Express 에서 외장 모듈을 사용하면 파일을 업로드할 수 있다.

파일을 업로드할 때는 멀티 파트(multipart) 포멧으로 된 파일 업로드 기능을 사용하며 파일 업로드 상태 등을 확인할 수 있다.


먼저 cat package.json 로 설치된 모듈 내용을 확인하자.

그림을 보면 multer 1.3.1 이 설치되어 있는 걸 확인할 수 있다.


express는 사용자가 업로드한 파일을 받아서 저장하는 기본 기능을 제공하지 않는다.

별도 모듈 multer 를 설치해줘야 한다.


multer 미들웨어 설치방법

npm install multer --save


아래 코드는 동영상강좌 내용으로 파일 업로드 테스트하고 나서 기록한 것이다.


 // Express 기본 모듈 불러오기
var express = require('express')
  , http = require('http')
  , path = require('path');

// Express의 미들웨어 불러오기
var bodyParser = require('body-parser')
  , cookieParser = require('cookie-parser')
  , static = require('serve-static')
  , errorHandler = require('errorhandler');

// 에러 핸들러 모듈 사용
var expressErrorHandler = require('express-error-handler');

// Session 미들웨어 불러오기
var expressSession = require('express-session');

// 파일 업로드용 미들웨어
var multer = require('multer');
var fs = require('fs');

//클라이언트에서 ajax로 요청 시 CORS(다중 서버 접속) 지원
var cors = require('cors');


// 익스프레스 객체 생성
var app = express();

// 기본 속성 설정
app.set('port', process.env.PORT || 3000);

// body-parser를 이용해 application/x-www-form-urlencoded 파싱
app.use(bodyParser.urlencoded({ extended: false }))

// body-parser를 이용해 application/json 파싱
app.use(bodyParser.json())

// public 폴더와 uploads 폴더 오픈
app.use('/public', static(path.join(__dirname, 'public')));
app.use('/uploads', static(path.join(__dirname, 'uploads')));

// cookie-parser 설정
app.use(cookieParser());

// 세션 설정
app.use(expressSession({
    secret:'my key',
    resave:true,
    saveUninitialized:true
}));


//클라이언트에서 ajax로 요청 시 CORS(다중 서버 접속) 지원
app.use(cors());


//multer 미들웨어 사용 : 미들웨어 사용 순서 중요  body-parser -> multer -> router
// 파일 제한 : 10개, 1G
var storage = multer.diskStorage({
    destination: function (req, file, callback) {
        callback(null, 'uploads')
    },
    filename: function (req, file, callback) {
        //callback(null, file.originalname + Date.now())
        var extension = path.extname(file.originalname);
        var basename = path.basename(file.originalname, extension);
        callback(null, basename + Date.now() + extension);
    }
});

var upload = multer({
    storage: storage, // storage 객체
    limits: {
        files: 10, // 한번에 업로드할 수 있는 파일 개수
        fileSize: 1024 * 1024 * 1024
    }
});


// 라우터 사용하여 라우팅 함수 등록
var router = express.Router();

// 파일 업로드 라우팅 함수
// photo.html 파일에서 action 요청 패스 : /process/photo
router.route('/process/photo').post(upload.array('photo', 1), function(req, res) {
    console.log('/process/photo 라우팅 함수 호출됨.');

    try {
        var files = req.files;

        console.dir('#===== 업로드된 첫번째 파일 정보 =====#')
        console.dir(req.files[0]);
        console.dir('#=====#');

        if(files.length > 0){
            console.dir(files[0]);

            // 현재의 파일 정보를 저장할 변수 선언
            var originalname = '',
                filename = '',
                mimetype = '',
                size = 0;

            if (Array.isArray(files)) {   // 배열에 들어가 있는 경우 (설정에서 1개의 파일도 배열에 넣게 했음)
                console.log("배열에 들어있는 파일 갯수 : %d", files.length);

                for (var i = 0; i < files.length; i++) {
                    originalname = files[i].originalname;
                    filename = files[i].filename;
                    mimetype = files[i].mimetype;
                    size = files[i].size;
                }
            }

            console.log('현재 파일 정보 : ' + originalname + ', ' + filename + ', '
                    + mimetype + ', ' + size);

            // 클라이언트에 응답 전송
            res.writeHead('200', {'Content-Type':'text/html;charset=utf8'});
            res.write('<h3>파일 업로드 성공</h3>');
            res.write('<hr/>');
            res.write('<p>원본 파일명 : ' + originalname + ' -> 저장 파일명 : ' + filename + '</p>');
            res.write('<p>MIME TYPE : ' + mimetype + '</p>');
            res.write('<p>파일 크기 : ' + size + '</p>');
            res.end();

        } else {
            console.log('파일이 없습니다');
        }
    } catch(err) {
        console.dir(err.stack);
    }

});

app.use('/', router);


// 404 에러 페이지 처리
var errorHandler = expressErrorHandler({
    static: {
      '404': './public/404.html'
    }
});

app.use( expressErrorHandler.httpError(404) );
app.use( errorHandler );


// Express 서버 시작
http.createServer(app).listen(app.get('port'), function(){
  console.log('Express server listening on port ' + app.get('port'));
});


 <!DOCTYPE html>
<html>
    <head>
        <meta charset="UTF-8">
        <title>파일업로드 테스트</title>
    </head>
<body>
    <h1>파일업로드</h1>
    <br>
    <form method="post" enctype="multipart/form-data" action="/process/photo">
        <table>
            <tr>
                <td><label>파일</label></td>
                <td><input type="file" name="photo" /></td>
            </tr>
        </table>
        <input type="submit" value="업로드" name="submit"/>
    </form>
</body>
</html>


블로그 이미지

Link2Me

,
728x90

IP Time 공유기를 사용하여 접속하면 보통 사설 IP 주소를 사용하게 된다.

내 PC의 공인 IP주소를 알아내는 코드는 Web 파싱을 이용하면 쉽다.


public static string getMyPublicIP()
{
    WebRequest request = WebRequest.Create("http://www.findip.kr");
    WebResponse response = request.GetResponse();
    StreamReader stream = new StreamReader(response.GetResponseStream());

    // 주소에 있는 텍스트 모두를 긁어 저장
    string firstStr = stream.ReadToEnd();
    // 파싱할 부분의 시작부분 검색
    int index1 = firstStr.IndexOf("<h1> 내 아이피 주소(My IP Address) :") + 31;
    // 끝부분 검색
    int index2 = firstStr.IndexOf("</h1>");
    //다시 담기
    string str = firstStr.Substring(index1, index2 - index1);
    return str;
}



블로그 이미지

Link2Me

,
728x90

RecyclerView 기본 개념을 자세히 다루지는 않는다.



    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
}


dependencies {
    implementation 'androidx.appcompat:appcompat:1.2.0'
    implementation 'com.google.android.material:material:1.2.1'
    implementation 'androidx.constraintlayout:constraintlayout:2.0.4'

    implementation 'gun0912.ted:tedpermission:2.0.0'
    implementation 'androidx.cardview:cardview:1.0.0'  // 레이아웃으로 사용할 CardView
    implementation 'androidx.recyclerview:recyclerview:1.1.0'
}

최신(2020.12월) 기준으로는 implementation 'androidx.recyclerview:recyclerview:1.1.0' 를 추가하면 된다.

아래 이미지의 import는 이전 자료 기준이라고 보면 된다.


Android Studio에서 RecyclerViewAdapter 를 쉽게 만드는 방법을 유투브 동영상을 보면서 알게되어 적어둔다.

Alt + Eneter 키를 눌러서 자동완성으로 만드는 방법이다.

RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.Holder> 처럼 파란색상으로 된 클래스명은 동일하게 적어준다.

가장 먼저 Holder 클래스를 추가하고 나서 Implement methods 를 추가추고 번호 순서대로 하면 쉽게 코드가 자동 완성된다.





이렇게 하고 나면

import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.view.ViewGroup;

class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.Holder> {
    @NonNull
    @Override
    public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return null;
    }

    @Override
    public void onBindViewHolder(@NonNull Holder holder, int position) {

    }

    @Override
    public int getItemCount() {
        return 0;
    }

    public class Holder extends RecyclerView.ViewHolder {
        public Holder(View itemView) {
            super(itemView);
        }
    }
}
 


모습으로 코드가 만들어진다.

여기에 실제 추가할 코드를 반영하여 작성한다.

import android.content.Context;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;

import java.util.ArrayList;

class RecyclerViewAdapter extends RecyclerView.Adapter<RecyclerViewAdapter.Holder> {
    Context mContext;
    private ArrayList<MessageData> mMessageData;

    public RecyclerViewAdapter(Context context, ArrayList<MessageData> arrayList) {
        mContext = context;
        mMessageData = arrayList;
    }

    @NonNull
    @Override
    public Holder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        return new Holder(LayoutInflater.from(parent.getContext()).inflate(R.layout.rv_layout,parent,false));
    }

    @Override
    public void onBindViewHolder(@NonNull Holder holder, int position) {
        MessageData data = mMessageData.get(position);
        holder.heading.setText(data.getHeading());
        holder.message.setText(data.getMessage());
    }

    @Override
    public int getItemCount() {
        return mMessageData.size();
    }

    public class Holder extends RecyclerView.ViewHolder {
        TextView heading;
        TextView message;
        public Holder(View itemView) {
            super(itemView);
            heading = itemView.findViewById(R.id.heading);
            message = itemView.findViewById(R.id.messageBody);
        }
    }
}
 


Acitivy.java 에서 구현할 코드

RecyclerView 관련 코드만 적었다.

public class MainActivity extends AppCompatActivity {
    Context context;
    private RecyclerView mRecyclerView;
    private RecyclerViewAdapter mAdapter;
    private ArrayList<MessageData> mMessageData = new ArrayList<>();

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = this.getBaseContext();

        initView();
    }

    private void initView(){
        mRecyclerView = findViewById(R.id.messagelist);
        mAdapter = new RecyclerViewAdapter(this,mMessageData);
        LinearLayoutManager manager = new LinearLayoutManager(this);
        DividerItemDecoration decoration = new DividerItemDecoration(this, manager.getOrientation());

        mRecyclerView.addItemDecoration(decoration);
        mRecyclerView.setLayoutManager(manager);
        mRecyclerView.setAdapter(mAdapter);
    }
}
 



블로그 이미지

Link2Me

,
728x90

경기버스 도착정보 서비스 API 를 http://www.data.go.kr 사이트에서 받아서 XML 데이터를 JSON 으로 파싱하는 예제를 테스트하고 적어둔다.



신청하면 2년간 사용할 수 있게 되어 있다.


프로젝트 build.gradle

allprojects {
    repositories {
        google()
        jcenter()
        maven { url "https://jitpack.io" }
    }
}


앱 build.gradle

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.github.bumptech.glide:glide:3.8.0' // 이미지 라이브러리
    implementation 'com.android.support:recyclerview-v7:27.1.0' // ListView 개선 버전
    implementation 'com.android.support:cardview-v7:27.1.0'
    implementation 'gun0912.ted:tedpermission:2.0.0'
    implementation 'com.github.smart-fun:XmlToJson:1.1.1'
    implementation 'com.android.volley:volley:1.1.0'
}


라이브러리 최신 정보는 https://github.com/smart-fun/XmlToJson 에서 확인해서 최신버전으로 적는게 좋다.



 public class MainActivity extends AppCompatActivity {
    Context context;
    public static final String TAG = MainActivity.class.getSimpleName();
    public EditText edit;
    public Button send;
    TextView status1;
    // http://www.data.go.kr 에서 로그인 후 API 조회하여 신청 승인받은 key 값
    String key = "iLOTMTnmawGIweE3%2F4DFwv98g0F3Vv7iJA%3D%3D"; // 실제 키값이 아님 (변경처리함)

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        context = this.getBaseContext();

        initView();
    }

    private void initView() {
        status1 = (TextView)findViewById(R.id.result); //파싱된 결과확인!
        edit = (EditText) findViewById(R.id.message) ;
        send = (Button)findViewById(R.id.send);

        // message send action
        send.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if(!edit.getText().toString().isEmpty()){
                    BusArriveTask(edit.getText().toString());
                    edit.setText(" ");
                }
            }
        });
    }

    private void BusArriveTask(String search){
        RequestQueue requestQueue = Volley.newRequestQueue(context);

        String StationId = null; // 정류소 ID
        try {
            StationId = URLEncoder.encode(search,"UTF-8");
        } catch (UnsupportedEncodingException e) {
            e.printStackTrace();
        }
        // 버스 도착정보 목록 조회
        String url = "http://openapi.gbis.go.kr/ws/rest/busarrivalservice/station?serviceKey="+key+"&stationId="+StationId+"";
        Log.d(TAG, "URL:"+url);

        StringRequest request= new StringRequest(Request.Method.GET, url,
                new Response.Listener<String>() {
                    @Override
                    public void onResponse(String response) {
                        XMLtoJSONData(response);
                    }
                },
                new Response.ErrorListener() {
                    @Override
                    public void onErrorResponse(VolleyError error) {

                    }
                });

        requestQueue.add(request);
    }

    private void XMLtoJSONData(String xml){
        // https://androidfreetutorial.wordpress.com/2016/11/28/how-to-convert-xml-to-json-for-android/
        XmlToJson xmlToJson = new XmlToJson.Builder(xml).build();
        // convert to a JSONObject
        JSONObject jsonObject = xmlToJson.toJson();
        Log.d(TAG, "jsonObject:"+jsonObject);

        // JSON 에서 배열은 [] 대괄호 사용, Objext 는 {} 중괄호 사용
        try {
            JSONObject response = jsonObject.getJSONObject("response");
            JSONObject msgHeader = response.getJSONObject("msgHeader");
            String resultCode = msgHeader.optString("resultCode");
            Log.d(TAG, "String resultCode :"+resultCode);

            if(resultCode.equals("0")){
                JSONObject msgBody = response.getJSONObject("msgBody");
                Log.d(TAG, "jsonObject msgBody :"+msgBody);

                JSONArray array = msgBody.getJSONArray("busArrivalList");
                for(int i=0; i < array.length();i++){
                    JSONObject obj = array.getJSONObject(i);
                    // optString which returns the value mapped by name if it exists
                    String plateNo1 =obj.optString("plateNo1"); // 첫번째 차량 번호
                    String locationNo1 =obj.optString("locationNo1"); // 첫번째 차량 위치 정보
                    String plateNo2 =obj.optString("plateNo2"); // 두번째 차량 번호
                    String locationNo2 =obj.optString("locationNo2"); // 두번째 차량 위치 정보
                    Log.d(TAG, "jString plateNo1 :"+plateNo1);
                    Log.d(TAG, "jString plateNo2 :"+plateNo2);
                    Log.d(TAG, "jString locationNo1 :"+locationNo1);
                    Log.d(TAG, "jString locationNo2 :"+locationNo2);
                }
            } else if(resultCode.equals("1")){
                Toast.makeText(context, "시스템 에러가 발생하였습니다", Toast.LENGTH_SHORT).show();
            } else if(resultCode.equals("4")){
                Toast.makeText(context, "결과가 존재하지 않습니다", Toast.LENGTH_SHORT).show();
            } else if(resultCode.equals("8")){
                Toast.makeText(context, "요청 제한을 초과하였습니다", Toast.LENGTH_SHORT).show();
            } else if(resultCode.equals("23")){
                Toast.makeText(context, "버스 도착 정보가 존재하지 않습니다", Toast.LENGTH_SHORT).show();
            }

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

}


로그캣에만 찍어보도록 코드를 구현한 상태다.




XML 메시지를 JSON 으로 변환하면 아래와 같이 된다.

중괄호를 JSONObject response = jsonObject.getJSONObject("response"); 로 파싱하고

로그캣을 찍어서 메시지를 확인하면서 범위를 좁혀 나가면 된다.

[] 대괄호가 나오는 부분에서는 JSONArray array = msgBody.getJSONArray("busArrivalList"); 로 파싱하고 for 문을 돌려서 원하는 결과를 추출할 수 있다.





공공데이터 파싱하는데 사용한 샘플 코드를 첨부 (RecyclerView 를 이용하여 화면에 뿌리는 예제로 구현)

BusInfo_sample.zip



제대로 만들어 보려고 한다면

부산버스 어플 소스 https://github.com/kmshack/Busanbus-Android 공개된 것으로 공부하면 많은 도움이 될 거 같다.

관련 기사  : http://www.bloter.net/archives/151935

블로그 이미지

Link2Me

,
728x90

SMACK 4.3.0 으로 OpenFire Server 에 로그인하는 기능을 적어둔다.

4.1.0 으로 해도 에러가 발생하고 ... 엄청난 삽질을 하고서 겨우 제대로 로그인되는 걸 확인했다.


build.gradle

repositories {
    maven {
        url 'https://oss.sonatype.org/content/repositories/snapshots'
    }
    mavenCentral()
}

dependencies {
    implementation fileTree(dir: 'libs', include: ['*.jar'])
    implementation 'com.android.support:appcompat-v7:27.1.1'
    implementation 'com.android.support.constraint:constraint-layout:1.1.3'
    implementation 'com.android.support:recyclerview-v7:27.1.1'
    implementation 'gun0912.ted:tedpermission:2.0.0'
    implementation 'com.google.code.gson:gson:1.7.2'
    implementation "org.igniterealtime.smack:smack-tcp:4.3.0"
    implementation "org.igniterealtime.smack:smack-extensions:4.3.0"
    implementation "org.igniterealtime.smack:smack-experimental:4.3.0"
    implementation ("org.igniterealtime.smack:smack-android:4.3.0"){
        exclude module:'smack-omemo'
        exclude module:'smack-omemo-signal'
    }
}
 



로그인이 성공되어야 채팅 기능을 만드는 걸 해볼 수 있는데 smack 4.2.0 으로 테스트하면 로그인에서 거부당한다.

아래 코드는 로그인이 성공적으로 잘 되는 걸 확인했으며, 동영상 예제를 따라하면서 적은 코드를 수정 보완한 것이라고 보면 된다.

따라서 구글링으로 검색된 다른 코드와는 약간 다르다. 즉 확장성이 떨어질 수 있다는 말이다.

로그인이 성공적으로 동작되도록 하는 코드라고 이해하면 된다.


Anroid 8.0에서 테스트를 했으며, 보안 강화로 IP주소를 직접 적으면 동작에 오류가 발생하는 거 같다.


private String loginURL = "abc.com"; // IP주소 적으면 오류 발생함 (The public/private ip address don't work)
private AbstractXMPPConnection mConnection;

public static final String TAG = MainActivity.class.getSimpleName();


private void setConnection() {
    // Create the configuration for this new connection
    new Thread() {
        @Override
        public void run() {
            StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
            StrictMode.setThreadPolicy(policy);

            InetAddress addr = null;
            try {
                addr = InetAddress.getByName(loginURL);
                Log.e(TAG,"addr : "+addr);
            } catch (UnknownHostException e) {
                e.printStackTrace();
            }
            HostnameVerifier verifier = new HostnameVerifier() {
                @Override
                public boolean verify(String hostname, SSLSession session) {
                    return false;
                }
            };
            DomainBareJid serviceName = null;
            try {
                serviceName = JidCreate.domainBareFrom(loginURL);
                Log.e(TAG,"serviceName : "+serviceName);
            } catch (XmppStringprepException e) {
                e.printStackTrace();
            }
            SmackConfiguration.DEBUG = true;
            XMPPTCPConnectionConfiguration config = XMPPTCPConnectionConfiguration.builder()
                    //.setUsernameAndPassword("test02", "test1234")
                    .setPort(5222)
                    .setSecurityMode(ConnectionConfiguration.SecurityMode.disabled)
                    .setXmppDomain(serviceName)
                    .setHostnameVerifier(verifier)
                    .setHostAddress(addr)
                    //.setDebuggerEnabled(true) // smack 4.3.0 error, smack 4.2.0 ok
                    .setSendPresence(true)
                    .build();

            SASLAuthentication.blacklistSASLMechanism("SCRAM-SHA-1");
            SASLAuthentication.blacklistSASLMechanism("DIGEST-MD5");
            SASLAuthentication.unBlacklistSASLMechanism("PLAIN");
            mConnection = new XMPPTCPConnection(config);
            mConnection.setReplyTimeout(10000);

            // Connect to the server
            try {
                Log.e(TAG,"mConnection.connect try");
                mConnection.connect();               
                mConnection.login("test01", "test1234");
                if (mConnection.isAuthenticated() && mConnection.isConnected()) {
                    // message send and receive code here.
                    Log.e(TAG,"run auth done and connected successfully");                    
                } else{
                    Log.e(TAG,"connected fail");
                }

            } catch (SmackException e) {
                e.printStackTrace();
            } catch (IOException e) {
                e.printStackTrace();
            } catch (XMPPException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }.start();
}



참고 사이트(reference)


'openfire' 카테고리의 다른 글

CentOS 6.6에 openfire 4.2.3 설치  (0) 2018.09.21
블로그 이미지

Link2Me

,