728x90

setContentView(R.layout.activity_main);


Java 소스파일에서 Android XML Layout 파일을 바로 여는 단축키는

Ctrl 를 누른 상태에서 마우스를 activity_main 에 가져다 대면 두개의 선택화면이 나온다.

여기에서 Open Declaration in layout/activity_main 을 선택하면

activity_main.xml 파일이 열린다.


블로그 이미지

Link2Me

,
728x90

SQL 에 대한 개념을 이해하려고 책도 참 여러권 샀다. 다 정독한건 아니고 필요한 곳만 찾아 본것이 많다.

하지만 난 아직 초보다. 프로그래머가 아니라 취미로 배우니 수준이 올라가지 않는다.

SQL 교육을 수강하고 책을 보면서 나름 터득한 걸 적어본다.

나중에 강의하게 되면 설명 쉽게 해줄 생각으로 좀 정리한 자료다.

 

SQL Aliases
- 칼럼명을 별칭(Aliases)으로 할 수도 있고, 테이블명을 별칭으로 할 수도 있다.

 

https://www.w3schools.com/sql/sql_join.asp 사이트를 참조하면 개념을 쉽게 이해할 수 있다.

영문이라 처음에는 이해가 잘 안될 수도 있다. 하지만 쿼리문을 계속 사용하다보면 이해가 될 것이다.

무엇이든 배우는데 가장 중요한 것은 개념에 대한 이해다. 개념을 이해하면 응용은 자연스럽게 된다.

 

 

 

select o.OrderID, c.CustomerName 등으로 select 절에 나열(Query 속도상 필요한 칼럼만 뽑는게 좋음)

Orders 테이블의 것만 조회하고 싶다면, select o.* from .... 이라고 하면 된다.

아래 그림의 교집합 안에서 조건에 맞는 걸 또 추출하고 싶다면, Where 조건절을 추가한다.

SELECT o.* FROM Orders o INNER JOIN Customers c ON o.CustomerID=c.CustomerID where o.CustomerID='userid'; // userid 는 PHP 에서 변수로 받은 거라고 보면 됨.

 

 

 

 

 

 

SELF JOIN 은 두개의 물리적인 테이블을 만들어서 원하는 결과를 뽑아내야 하지만, 그럴 필요없이 메모리 공간에 하나의 논리적인 테이블을 만들어서, 물리적인 테이블과 조인하여 결과를 추출하는 것이라고 이해하면 된다.

 

 

 

 

 

Update FavoriteBook f INNER JOIN COMMON c ON f.grpidx=c.grpidx SET f.grpNM=c.grpNM where f.grpidx=2;

- FavoriteBook f INNER JOIN COMMON c ON f.grpidx=c.grpidx : 한개의 테이블이라고 생각한다.

- Update 테이블명 SET  WHERE 조건

- 오라클 쿼리방식으로 한다면

  Update FavoriteBook f, COMMON c SET f.grpNM=c.grpNM where f.grpidx=c.grpidx and f.grpidx=2;

FavoriteBook f INNER JOIN COMMON c ON f.grpidx=c.grpidx

출처: http://link2me.tistory.com/1201 [소소한 일상 및 업무TIP 다루기]

 

순수하게 A만 뽑아주는 쿼리

 

SELECT a.id, a.name, a.age, b.mobile, b.internet 
FROM a LEFT JOIN b on a.id = b.id 
WHERE b.id is null
order by a.id ASC

 

MySQL 은 FULL OUTER JOIN 은 없다.

LEFT JOIN 과 RIGHT JOIN 을 UNION 으로 묶어서 처리하면 된다.

 

간단하게 정리한 것이지만 도움되면 댓글이나 공감 눌러주세요.

'SQL' 카테고리의 다른 글

MySQL 두 테이블 불일치 데이터 찾는 SQL  (0) 2018.08.18
Toad for MySQL - Database Diagram  (0) 2017.06.02
[MySQL] 정규식을 활용한 검색 (REGEXP)  (0) 2016.12.11
MySQL 컬럼 순서 바꾸기  (0) 2016.12.10
[Oracle] PL/SQL  (0) 2016.11.24
블로그 이미지

Link2Me

,
728x90

안드로이드 스튜디오를 간편하게 설치하는 방법에 대해 알아보자.

안드로이드 스튜디오, Eclipse는 설치된 폴더를 그대로 다른 PC나 드라이브로 옮겨도 이용할 수 있다.

옮긴 경우 설정된 경로 정보만 설정해주면 바로 이용 가능하다.

단, PC가 교체된 경우라면, Java JDK를 먼저 설치해주고, PATH 경로를 설정해주어야 한다.

 

설치할 때 주의할 점은 디렉토리명에 절대 한글이 들어가면 안된다. (non-ASCII)

대다수 개발 입문자들이 Users 폴더에 자동 설치를 하는데 권장하고 싶지 않다.

아래 그림 예시처럼 구분하면 편하다. (C 드라이브 용량이 부족한 경우에는 외장 SSD 등 다른 디렉토리에 구성하면 된다.)

Android/Android Studio

Android/android-sdks

 

 

 

ㅇ android-sdk 는 eclipse 와 android studio 둘다 공용으로 사용할 수 있다.

    - 별도로 구분해서 설치하는게 좋다.

    - 공용으로 사용하다보니 동작이 안되는게 있더라.

 

ㅇ Android Studio 폴더 파일만 복사해서 C드라이드 특정 폴더에 붙여넣기만 해도 된다.

    (이미 설치된 PC에서 복사 또는 다른 사람이 설치한 걸 복사해서 내 PC에 붙여넣기)

    그런 다음에 bin 폴더에서 studio64.exe 파일을 실행해서 sdk 연결정보를 등록해준다.

 

ㅇ 수동 설치방법

    - 설치방법은 압축된 폴더를 하드디스크(HDD) 공간이 넉넉한 드라이브(D드라이드 등)에서 압축을 푼다.

    - android-sdks 라는 폴더를 만든다.

    - Android Studio/bin/studio64.exe 파일을 실행해서 수동으로 SDK 파일을 받는다

    - 설치된 윈도우 운영체제가 32비트 환경이면 studio.exe 파일을 실행한다.

 

 

 

 


 

 

 

 

 

 

 

 

기존 sdk 로 경로 설정을 변경하고 최신 sdk를 설치했다.

 

Android Studio 버전이 Up되면서 Setting 해주는 부분이 변경되었다.

https://link2me.tistory.com/2030 참조하면 도움될 것이다.

블로그 이미지

Link2Me

,
728x90

필요한 코드를 Part 단위로 기록하기 위해 작성한다.


MySQL DB 테이블에서 전화번호를 전부 -가 들어간 것을 변경했다.

Update Person SET mobile=replace(mobile,'-','');


안드로이드 코드

public String filterPhoneNO(String number){
    final Pattern PAT_COUNTRY_CODE_KOREA = Pattern.compile("^(\\+|\\-)?82\\-?");
    number = number.replaceAll("-", ""); // - 전부 제거
    if(number == null || number.length() == 0) {
        return null;
    }
    if (!(number = PAT_COUNTRY_CODE_KOREA.matcher(number).replaceFirst("")).startsWith("0"))
    {
        number = '0' + number;
    }
    if(number.matches("(01[016789]{1})(\\d{3,4})(\\d{4})")){
        return number.replaceAll("(\\d{3})(\\d{3,4})(\\d{4})", "$1-$2-$3");
    } else if(number.matches("(02)(\\d{3,4})(\\d{4})")){
        return number.replaceAll("(\\d{2})(\\d{3,4})(\\d{4})", "$1-$2-$3");
    } else {
        return number.replaceAll("(\\d{3})(\\d{3,4})(\\d{4})", "$1-$2-$3");
    }
}


서버에서 읽어온 정보를 메모리에 저장

protected void showList() {
    // 서버에서 읽어온 정보를 mAdapter 에 저장하고 화면에 출력
    try { 
        JSONObject jsonObj = new JSONObject(myJSON); 
        peoples = jsonObj.getJSONArray(TAG_RESULTS);
       
        for(int i=0;i<peoples.length();i++){ 
        JSONObject c = peoples.getJSONObject(i); 
        String uid = c.getString(TAG_UID); 
        String name = c.getString(TAG_NAME); 
        String mobile = filterPhoneNO(c.getString(TAG_Mobile));
        Drawable myIcon = getResources().getDrawable( R.drawable.ic_launcher );
       
        mAdapter.addItem(myIcon,uid,name,mobile);
        }
       
        runOnUiThread(new Runnable() {
        @Override
        public void run() {
            mAdapter.notifyDataSetChanged();
        }
        });
       
    } catch (JSONException e) { 
        e.printStackTrace(); 
    }




블로그 이미지

Link2Me

,
728x90

loginChk.php 파일의 경로가 /abc/pages/loginChk.php 로 되어 있다.


로그인 모달창에서 로그인 처리를 하는데 경로설정에 대한 문제로 골치가 아팠다.

유연한 상대경로 설정하는 방법을 몰라서 겪은 증상이다.


아래와 같이 경로에 PHP의 상대경로를 직접 입력하면 문제가 될까? 안될까?

이렇게 입력하면 ajax 에서는 경로를 인식하지 못한다.


자동 상대경로 설정법은 http://link2me.tistory.com/1197 를 참조하면 된다.


// 로그인 처리
$('#login-submit').click(function(e){
    e.preventDefault();
    //alert(window.location.pathname); // 현재 경로 확인
    $.ajax({
        url:
<?php echo $g['path_page'];?>"+'loginChk.php',
        type: 'POST',
        data: {userid:$('#userid').val(), password:$('#password').val()},
        dataType: "json",
        success: function (response) {
           if(response.result == 1){
               //alert('로그인 성공');
               $('#modal-login').modal('hide');
               location.reload(); // 화면 갱신
           } else {
               alert('로그인 실패');
           }
        },
        error: function(jqXHR, textStatus, errorThrown){
           alert("arjax error : " + textStatus + "\n" + errorThrown);
        }
    });
});


해결방법

PHP의 변수를 ajax 등 javascript 로 넘기기 위해서는 HTML 로 일단 저장해야 한다.

<body></body> 사이에 아래 코드를 추가한다.

내용은 전혀 보이지 않게 처리한다.

<div id="ajax_loginpath" data-path="<?php echo $g['path_page'];?>" ></div>


그런 다음에 아래와 같이 코드를 구현하면 해결된다.

// 로그인 처리
$('#login-submit').click(function(e){
    e.preventDefault();
    //alert(window.location.pathname); // 현재 경로 확인
    var loginpath =$("#ajax_loginpath").attr('data-path');
    $.ajax({
        url: loginpath+'loginChk.php',
        type: 'POST',
        data: {userid:$('#userid').val(), password:$('#password').val()},
        dataType: "json",
        success: function (response) {
           if(response.result == 1){
               //alert('로그인 성공');
               $('#modal-login').modal('hide');
               location.reload(); // 화면 갱신
           } else {
               alert('로그인 실패');
           }
        },
        error: function(jqXHR, textStatus, errorThrown){
           alert("arjax error : " + textStatus + "\n" + errorThrown);
        }
    });
});


결론 정리

상대경로의 개념은 현재 실행되는 파일을 기준으로 경로(path)를 상대적으로 설정한다.

따라서 실행되는 파일의 위치가 달라지면 그 위치를 기준으로 경로를 상대적으로 설정하기 때문에

../../loginChk.php 처럼 적어주면 문제가 생긴다.

실행되는 파일의 위치가 어디든지 간에 상대경로 문제가 아무런 문제없이 해결되기 위해서는 코드를 잘 짜야 한다.

이런 문제때문에 계속 문제에 부딪치면서 완벽한 해결책을 구현해보겠다는 일념으로 드디어 해결을 했다.

자동 상대경로 설정법은 http://link2me.tistory.com/1197 과 이 게시글을 활용하면 누구든지 경로설정 문제로 골치아픈 일은 없을 것이다.

블로그 이미지

Link2Me

,
728x90

현재 경로 설정을 매번 수동으로 일일이 지정하려니까 귀찮아서 구글링으로 찾은 소스를 수정해서 구현했다.

구글에서 찾은 함수 pathUrl 는 http://localhost/ 라는 명칭을 찾아준다.

이런 방법으로 경로를 지정하면 보안 강화로 인해서 url forwarding 문제가 생긴다.


파일을 include 해서 사용하다보니 경로 설정에서 문제가 생기더라.

현재 실행되는 파일의 위치를 기준으로 경로를 인식해서 처리한다.

그러므로 홈페이지의 소스가 설치된 기준 위치를 어디로 정할 것인가를 고려해서 사용하면 된다.


<?php
$cur_root = pathUrl();
$g = array(
    'path_root'   => $cur_root,
    'path_core'   => $cur_root.'_core/',
    'path_config'   => $cur_root.'_core/config/',
    'path_tmp'    => $cur_root.'_tmp/',
    'path_layout' => $cur_root.'layout/',
    'path_module' => $cur_root.'modules/',
    'path_plugin' => $cur_root.'plugin/',
    'path_bootstrap' => $cur_root.'plugin/bootstrap/',
    'path_page'   => $cur_root.'pages/',
    'path_file'   => $cur_root.'files/'
);

function pathUrl($dir = __DIR__){
    $root = "";
    $dir = str_replace('\\', '/', realpath($dir));

    //HTTPS or HTTP
    $root .= !empty($_SERVER['HTTPS']) ? 'https' : 'http';

    //HOST
    $root .= '://' . $_SERVER['HTTP_HOST'];
    $web_root = $root;

    //ALIAS
    if(!empty($_SERVER['CONTEXT_PREFIX'])) {
        $root .= $_SERVER['CONTEXT_PREFIX'];
        $root .= substr($dir, strlen($_SERVER[ 'CONTEXT_DOCUMENT_ROOT' ]));
    } else {
        $root .= substr($dir, strlen($_SERVER[ 'DOCUMENT_ROOT' ]));
    }

    $root = substr($root,strlen($web_root),strlen($root));
    //$root .= '/';

    // 홈페이지 기준점을 http://domain/abc/ 로 설정한 경우에는 -2 를 해준다.
    $cnt = count(explode("/",$root)) -2; // 현재 경로 구하기

    switch($cnt){  // 상대경로 설정
        case 0:
            $cur_root = './';
            break;
        case 1:
            $cur_root = '../';
            break;
        case 2:
            $cur_root = '../../';
            break;
        case 3:
            $cur_root = '../../../';
            break;
        case 4:
            $cur_root = '../../../../';
            break;
        case 5:
            $cur_root = '../../../../../';
            break;
    }

    return $cur_root;
}
?>



사용법 예제

<?php
if(!isset($_SESSION)) {
    session_start();
}
require_once 'path.php';
session_save_path($g['path_tmp'].'session');

require_once $g['path_config'].'dbconnect.php';
require_once $g['path_config'].'config.php';

if(isset($_SESSION['userid'])){
    $userid = $_SESSION['userid'];
}

$m = isset($_GET['m'])? $_GET['m']:'';
?>

블로그 이미지

Link2Me

,
728x90

ajax 로 form 값을 전송하는데 정상적으로 처리가 안된다.

이것 때문에 몇시간을 삽질했다. 네이버 검색하면 메시지 자체가 검색되지 않는다.

구글링을 하면 검색결과가 많은데 제대로 된 해결책을 찾기가 쉽지 않다.


error

 parsererror,SyntaxError: JSON.parse: unexpected character at line 1 column 1 of the JSON data

 증상

 error 메시지는 나오는데 DB 테이블에는 정보가 업데이트되고 있음

 원인

 함수에서 warning 메시지 발생으로 인한 문제로 판명됨

 조치

 아래에 자세히 기술


$.ajax({
    url: 'memberInfo.php',
    type: 'POST',
    data: {
        uid:$("#memberuid").val(),
        userNM:$("#memberName").val(),
        mobileNO:$("#memberMobile").val()
    },
    dataType: "json",
    success: function (response) {
        if(response.result == 1){
            alert('수정 완료');
            location.replace('index.php'); // 화면 갱신
        }
        if(response.result == 0){
            alert('수정 실패');
        }
    },
    error: function(jqXHR, textStatus, errorThrown){
        alert("arjax error Type : " + textStatus + "\n" + errorThrown);
    }
});


데이터가 정상적으로 넘어가는지 확인하려고 alert 으로 일일이 찍어봤다.

정상적으로 잘 넘어간다.


그래서 memberInfo.php 파일에서 변수가 넘어왔다고 가정하고 직접 실행을 해봤으나, 정상적이다.

테스트로 값을 넘기는 거라 임의의 값으로 처리하면서 DB 저장되는 건 주석처리를 해버렸다.

<?php
if(!isset($_SESSION)) {
    session_start();
}

//$_POST['userNM'] ='홍길동';
//$_POST['mobileNO'] ='010-123-7777';

if (empty($_POST)) {
    echo json_encode(array('result'=>'0'));
    exit;
} else {
    @extract($_POST);
    include_once $_SERVER['DOCUMENT_ROOT'].'/_core/config/config.php';
    include_once $_SERVER['DOCUMENT_ROOT'].'/_core/config/dbconnect.php'; // db접속
    include_once $_SERVER['DOCUMENT_ROOT'].'/phpclass/dbClass.php';
    $u=new MySQLiDbClass;

    $mobileNO = preg_replace("/[^0-9]/", "", $mobileNO); //전화번호 숫자이외에 제거
    $QSET ="userNM='".$userNM."',";
    $QSET.="mobileNO='".$mobileNO."'";
    $u->getDbUpdate('member_id',$QSET,"uid=".$uid);
    echo json_encode(array('result'=>'1'));
}

?>


DB에는 제대로 저장되는지 확인해봤다.

DB 테이블에는 정상적으로 update가 되고 있다.

단지 화면상으로 결과를 돌려주는 부분에서 에러를 발생시키고 있다.


문제 해과정

1단계 :

dataType: "json" 을 dataType: "text" 로 변경하고

success : function(response) { alert(response); 를 추가한다.

그러면 아래와 같이 출력되는 메시지를 전부 보여준다.


<br />
<b>Warning</b>:  mysqli_query() expects at least 2 parameters, 1 given in <b>C:\AutoSet9\public_html\workspace\phpclass\dbClass.php</b> on line <b>184</b><br />
<br />
<b>Warning</b>:  mysqli_query() expects at least 2 parameters, 1 given in <b>C:\AutoSet9\public_html\workspace\phpclass\dbClass.php</b> on line <b>185</b><br />
{"result":"1"}


2단계 :

Warning 이 발생한 부분을 찾아서 수정해준다.


//DB업데이트
function getDbUpdate($table,$set,$where){
    global $dbconn;
    mysqli_query($dbconn,'set names utf8');
    mysqli_query($dbconn,'set sql_mode=\'\'');
    $sql ="update ".$table." set ".$set.($where?' where '.$this->getSqlFilter($where):'');
    mysqli_query($dbconn,$sql);
    return 1;
}


3단계 :

dataType: "json" 으로 원복 조치


$rs=$d->getDbUpdate('member_id',$QSET,"uid=".trim($uid));
if($rs == 1){
    echo json_encode(array('result'=>'1'));
}else{
    echo json_encode(array('result'=>'0'));
}   


원인은 mysqli_query 부분을 제대로 입력하지 않아서 경고메시지가 출력되었던 것이었다.

결과값이 1이면 처리하라는 if문의 결과값만 돌려줄 줄 알았는데 그게 아니었다.

화면에 출력되는 경고메시지가 있다보니, echo json_encode(array('result'=>'1')); 메시지 이외의 값도 결과로 돌려주었었나 보다.

함수 하나를 이용하더라도 완벽하게 만들어서 이용해야 한다는 걸 다시 한번 느꼈다.

블로그 이미지

Link2Me

,
728x90

window.location.href; // URL 포함한 전체 경로


window.location.href = 'index.php';

location.replace('index.php'); // 이전 페이지로 돌아가기 할 수 없음


window.location.pathname; // 현재 경로 확인


window.location.pathname.split('/').pop(); // 현재 실행 파일명


location.reload() and location.reload(true) works like F5 on browser. (페이지를 재실행)

window.location.reload() : 컴퓨터에 이미 받아놓은 캐쉬파일을 뒤지고 없으면 서버에서 파일을 받아온다.
window.location.reload(true) : true 가 들어가게 되면 캐쉬파일은 무시하고 무조건 서버에서 받아온다.


// 회원가입 처리를 위한 화면 이동
$('#member-join').click(function(){
    $('#modal-login').modal('hide'); // 모달 창 닫기
    //alert(window.location.pathname); // 현재 경로 확인
    if(window.location.pathname.split('/').pop() != 'member_join.php'){ // 현재 실행 파일명
        location.replace('member_join.php');
    }
});



// 부모창의 form안에 자동으로 값을 채워넣고 그 값을 submit
window.opener.document.getElementById("parent_hidden_field_something").value = "somethingSpecial";
window.opener.document.getElementById("parent_form_something").submit();




블로그 이미지

Link2Me

,
728x90

메뉴를 수동으로 일일이 작성하지 않고 PHP/MySQL 과 연동하여 테이블 정보를 자동으로 읽어서 메뉴가 생성되도록 하는 방법이다.


=== index.php ===

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="robots" content="noindex,nofollow"/>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="pragma" content="no-cache" />
    <meta name="apple-mobile-web-app-capable" content="no" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <title>Multi level dropdown menu in Bootstrap 3</title>
    <link rel="stylesheet" href="css/topmenu.css" />
    <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" />
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" />
    <script src="http://code.jquery.com/jquery.min.js" ></script>
    <script src="bootstrap/js/bootstrap.min.js"></script>
    <!--[if lt IE 9]> <!-- 5. 인터넷익스플로러 9버전 이하일 경우 html5가 인식될 수 있게 해주는 스크립트 -->
    <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <script src="bootstrap/js/respond.min.js"></script>
    <script src="js/topmenu.js"></script>
</head>
<body>
<nav class="navbar navbar-default" role="navigation">
    <div class="container">
        <div class="navbar-header">
            <button type="button" class="navbar-toggle" data-toggle="collapse" data-target="#navbar-collapse-1">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <a class="navbar-brand" href="index.php">Home</a>
        </div>
        <div class="collapse navbar-collapse" id="navbar-collapse-1">
            <ul class="nav navbar-nav">
                <?php require_once 'menu.php';?>
            </ul>
        </div>
    </div>
</nav>
</body>
</html>


=== menu.php ===

<?php
require_once 'dbconnect.php';
$sql = "SELECT * FROM menu_items";
$res = mysqli_query($dbconn, $sql);
while($row = mysqli_fetch_assoc($res)){
    $menu[] = $row;
}

$top_menu = bootstrap_menu($menu);
echo $top_menu;

function bootstrap_menu($array,$parent_id = 0,$parents = array())
{
    if($parent_id==0) {
        foreach ($array as $element) {
            if (($element['parent_id'] != 0) && !in_array($element['parent_id'],$parents)) {
                $parents[] = $element['parent_id'];
            }
        }
    }
    $php_menu = '';
    foreach($array as $element) {
        if($element['parent_id']==$parent_id) {
            if(in_array($element['id'],$parents)) {
                $dropdown_sub=$parent_id > 0 ? 'dropdown-submenu':'';
                $php_menu .= '<li class="dropdown '.$dropdown_sub.'">';
                $php_menu .= '<a href="'.$element['url'].'" class="dropdown-toggle" data-toggle="dropdown">'.$element['name'].' <span class="caret"></span></a>';
            } else {
                $php_menu .= '<li>';
                $php_menu .= '<a href="' . $element['url'] . '">' . $element['name'] . '</a>';
            }
            if(in_array($element['id'],$parents)) {
                $php_menu .= '<ul class="dropdown-menu">';
                $php_menu .= bootstrap_menu($array, $element['id'], $parents);
                $php_menu .= '</ul>';
            }
            $php_menu .= '</li>';
        }
    }
    return $php_menu;
}

?>


=== topmenu.css ===

/* dropdown submenu */
.dropdown-submenu{position:relative;}
.dropdown-submenu>.dropdown-menu{
    top:0;left:100%;
    margin-top:-6px;
    margin-left:-1px;
    -webkit-border-radius:0 6px 6px 6px;
    -moz-border-radius:0 6px 6px 6px;
    border-radius:0 6px 6px 6px;
}
.dropdown-submenu>a:after{
    display:block;
    content:" ";
    float:right;
    width:0;
    height:0;
    border-color:transparent;
    border-style:solid;
    border-width:5px 0 5px 5px;
    border-left-color:#cccccc;
    margin-top:5px;
    margin-right:-10px;
}
.dropdown-submenu:hover>a:after{
    border-left-color:#555;
}
.dropdown-submenu.pull-left{float:none;}
.dropdown-submenu.pull-left>.dropdown-menu{
    left:-100%;
    margin-left:10px;
    -webkit-border-radius:6px 0 6px 6px;
    -moz-border-radius:6px 0 6px 6px;
    border-radius:6px 0 6px 6px;
}

@media only screen and (max-width: 768px) {
   
}

@media only screen and (min-width: 768px) {

    /* 스마트폰에서는 동작하지 않게 */

    .dropdown-submenu:hover>.dropdown-menu {
        display: block;
    }
}


=== topmenu.js ===

$(function(){
    $('ul.dropdown-menu [data-toggle=dropdown]').on('click', function(event) {
        event.preventDefault();
        event.stopPropagation();
        $(this).parent().siblings().removeClass('open');
        $(this).parent().toggleClass('open');
    });
    // 현재 선택한 메뉴 : active
    $("ul li").click(function() {
        // remove classes from all
        $("ul .active").removeClass("active");
        // add class to the one we clicked
        $(this).addClass("active");
    });

});


=== db 스키마 ===

CREATE TABLE IF NOT EXISTS `menu_items` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `parent_id` varchar(11) DEFAULT NULL,
  `name` varchar(200) NOT NULL,
  `url` varchar(200) NOT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM  DEFAULT CHARSET=utf8 AUTO_INCREMENT=12 ;


INSERT INTO `menu_items` (`id`, `parent_id`, `name`, `url`) VALUES
(1, '0', 'Daum', 'http://www.daum.net'),
(2, '0', 'Java', '#'),
(3, '0', 'PHP', '#'),
(4, '3', 'PHP Tutorial', '#'),
(5, '3', '사이트', '#'),
(6, '5', '티스토리', '#'),
(7, '2', 'Java Tutorial', '#'),
(8, '2', 'Java Resources', '#'),
(9, '2', '테이블', 'table.php'),
(10, '5', '네이버', 'http://www.naver.com'),
(11, '3', 'PHP Resources', '#');


메뉴 자동으로 생성하는 것은 구글에서 treemenu 를 검색하면 나온다.

이걸 부트스트랩 기반에 적용하는 것이다.

관리자 화면에서 메뉴를 자동으로 생성하는 것은 아직 완벽하게 만들어놓지 않아서 작성하지 않는다.

다만, 참조할 게시글만 적어둔다.


http://link2me.tistory.com/1146


http://www.phpzag.com/ 사이트에 보면 도움된다.

그리고 구글링으로 jstree 를 검색하면 나온다.

http://phpflow.com/php/dynamic-tree-with-jstree-php-and-mysql/


기존 treemenu를 부트스트랩 기반으로 연동하는 걸 테스트 하는 중이다.

jQuery 기반 동작 소스를 부트스트랩 기반으로 완벽하게 처리할 때까지 시도해볼 생각이다.

블로그 이미지

Link2Me

,
728x90

부트스트랩 기반으로 멀터레벨 드랍다운 메뉴를 만드는 걸 목표로 구글링해서 찾은 소스를 분석해가면서 만들어보려고 한다.


http://bootsnipp.com/snippets/featured/multi-level-dropdown-menu-bs3

에 나온 예제 소스를 가지고 분석해보자.


사이트 HTML 소스만으로 실행하면 아래 화면과 같은 결과가 나온다.

소스 보기


이 소스에서 bootstrap 관련 CSS와 JS를 연결하면, 아래와 같이 바뀐다.


소스보기


서브메뉴가 제대로 나오지 않는다는 걸 확인할 수 있다.

이제 topmenu.css 라는 이름으로 css 파일을 추가해보자. 

서브메뉴까지 다 나온 걸 확인할 수 있다.

여기까지 시도하려고 작성한 것은 아니다.

분석을 더 해보자.

<div class="container">
    <div class="row">
        <h2>Multi level dropdown menu in Bootstrap 3</h2>
        <hr>
        <div class="dropdown">
            <a id="dLabel" role="button" data-toggle="dropdown" class="btn btn-primary" data-target="#" href="/page.html">
                Dropdown <span class="caret"></span>
            </a>
            <ul class="dropdown-menu multi-level" role="menu" aria-labelledby="dropdownMenu">
              <li><a href="#">Some action</a></li>
              <li><a href="#">Some other action</a></li>
              <li class="divider"></li>
              <li class="dropdown-submenu">
                <a tabindex="-1" href="#">Hover me for more options</a>
                <ul class="dropdown-menu">
                  <li><a tabindex="-1" href="#">Second level</a></li>
                  <li class="dropdown-submenu">
                    <a href="#">Even More..</a>
                    <ul class="dropdown-menu">
                        <li><a href="#">3rd level</a></li>
                        <li><a href="#">3rd level</a></li>
                    </ul>
                  </li>
                  <li><a href="#">Second level</a></li>
                  <li><a href="#">Second level</a></li>
                </ul>
              </li>
            </ul>
        </div>
    </div>
</div>



<ul class="dropdown-menu"> 로 표기되고 있고,
<li class="dropdown-submenu"> 로 표기되고 있다.


=== topmenu.css ===

.dropdown-submenu { position: relative;}

.dropdown-submenu>.dropdown-menu {
    top: 0;
    left: 100%;
    margin-top: -6px;
    margin-left: -1px;
    -webkit-border-radius: 0 6px 6px 6px;
    -moz-border-radius: 0 6px 6px;
    border-radius: 0 6px 6px 6px;
}

.dropdown-submenu:hover>.dropdown-menu { display: block;}

.dropdown-submenu>a:after {
    display: block;
    content: " ";
    float: right;
    width: 0;
    height: 0;
    border-color: transparent;
    border-style: solid;
    border-width: 5px 0 5px 5px;
    border-left-color: #ccc;
    margin-top: 5px;
    margin-right: -10px;
}

.dropdown-submenu:hover>a:after { border-left-color: #fff;}

.dropdown-submenu.pull-left { float: none;}

.dropdown-submenu.pull-left>.dropdown-menu {
    left: -100%;
    margin-left: 10px;
    -webkit-border-radius: 6px 0 6px 6px;
    -moz-border-radius: 6px 0 6px 6px;
    border-radius: 6px 0 6px 6px;
}


메뉴를 수동으로 직접 작성하는 것은 별로 어려울 것이 없을 거 같다.

목적은 메뉴를 PHP와 연동하여 자동으로 생성하는 걸 구현하고 싶다.

다음에는 PHP 와 연동하여 메뉴를 자동으로 생성하는 걸 해보자.

블로그 이미지

Link2Me

,
728x90

<?php
if(isMobile()){
    //include_once("m.php");
    echo 'Mobile';
}else{
    //include_once("pc.php");
    echo 'Windows';
}

// 접속 디바이스가 모바일인지 검사
function isMobile() {
        return preg_match("/(android|avantgo|blackberry|bolt|boost|cricket|docomo|fone|hiptop|mini|mobi|palm|phone|pie|tablet|up\.browser|up\.link|webos|wos)/i", $_SERVER["HTTP_USER_AGENT"]);
}
?>


블로그 이미지

Link2Me

,
728x90

아직 bootstrap 내용을 완벽하게 숙지하지 못한 상태에서 기능 익히느라고 Copy & Paste 하면서 형태를 변경해보면서 하다보니 원하지 않은 결과로 좀 고생하고 있다.


<nav class="navbar navbar-default navbar-fixed-top">

를 설정했는데 모달(modal)창이 계속 비활성화되어 왜 그런지 원인 찾으려고 무진장 고생을 했다.


navbar-fixed-top 때문에 생긴 현상이다.

이거 없앴더니 바로 정상 모드로 돌아온다.


if(tabName == '#modal-login'){
    //alert(tabName);
    $('#modal-login').on('shown.bs.modal', function () {
                $(this).find('input[type=text]:visible:first').focus();
    });
}


그리고 Path 설정을 하면서 고생 좀 하고 있는 중이다.

Web root 에 설정하면 문제가 없는데 실행되는 파일이 서브디렉토리에 있으면 문제가 되는거 같다.

현재 디렉토리 경로를 파악하여 어떤 경로에 있던 완벽하게 할 수 있는 코드를 만들어 볼 생각이다.


<?php
$cur_root = '../';  // 실행경로가 root 가 아니라서 root는 부모 디렉토리
$g = array(
    'path_root'   => $cur_root,
    'path_core'   => $cur_root.'_core/',
    'path_var'    => $cur_root.'_var/',
    'path_tmp'    => $cur_root.'_tmp/',
    'path_layout' => $cur_root.'layout/',
    'path_module' => $cur_root.'modules/',
    'path_plugin' => $cur_root.'plugin/',
    'path_bootstrap' => $cur_root.'plugin/bootstrap/',
    'path_page'   => $cur_root.'pages/',
    'path_file'   => $cur_root.'files/'
);

?>








블로그 이미지

Link2Me

,
728x90

var email = $('#email').val();
if(email== ''){
    alert('이메일주소를 입력하세요');
    email.focus();
    return false;
} else {
    var emailRegex = /^([\w-]+(?:\.[\w-]+)*)@((?:[\w-]+\.)*\w[\w-]{0,66})\.([a-z]{2,6}(?:\.[a-z]{2})?)$/i;
    if (!emailRegex.test(email)) {
        alert('이메일 주소가 유효하지 않습니다. ex)abc@gmail.com');
        email.focus();
        return false;
    }
}



블로그 이미지

Link2Me

,
728x90

EditPlus 는 인터넷에서 구한 소스 및 텍스트를 정리하는데 최고로 편하다.

이런 작업을 할 때 반복적으로 해야 할 경우와 여러번  실행해야 하는 불편을 키 한번만 누르면 자동으로 실행되게 하는 편리한 기능이 장점 중 하나다.


빈줄지우기 와 줄끝 공백지우기를 한꺼번에 처리하는 걸 매크로 단축키로 지정했다.





여기까지 진행하고 나면 Ctrl + Q 를 눌러주면 매크로 녹음기능이 끝난다.

이후부터는 단축키로 지정한 Alt + 2 만 눌러주면 해당 설정한 매크로가 자동으로 실행된다.

블로그 이미지

Link2Me

,
728x90

Aptana 에서 프로젝트(PHP Project)를  생성하고, 화면에 작업하는 모습이다.


왼쪽 1번은 생성한 프로젝트가 표시되고 현재 작업중인 프로젝트는 열린 상태로 보인다.

2번은 <div></div> 가 어디에서 어디까지 영역이 미치는지를 마우스로 찍으면 화면상에 표시를 해주어 편하다.


작업창 화면 색상 표시 부분 설정은

Window --> Preference 에서 여러가지 테마를 설정할 수 있다.

내가 선택한 테마는 Pastels on Dark 이다.




파일이 제대로 동작하는지 AutoSet9 상에서 보고자 한다면

환경설정에서 연결한 Firefox 브라우저 설정에 따라 자동으로 Firefox 웹 창이 뜨면서 보여준다.




블로그 이미지

Link2Me

,
728x90

테이블에 대한 사항은 별도로 table-01.php 파일로 분리했다.

코드가 너무 복잡해 보이는 경향을 줄이기 위함이다.


=== index.php ===

<?php
require_once 'dbconnect.php'; // db접속 성공
require_once 'phpclass/dbClass.php';
require_once 'phpclass/boardiClass.php';

$c = new MySQLiDbClass();

$link_url = $_SERVER['PHP_SELF']; // 현재 실행중인 파일명 가져오기
$rowsPage = 12;

// 화면에 출력할 칼럼 발췌
$flddata ="uid,ItemName,Price,Quantity";
$where ="";
$curPage = isset($_GET['p']) ? $_GET['p'] : 1;
$result = $c->getDbArray('items',$where,$flddata,'',$rowsPage,$curPage);
$totalcnt = $c->getDbRows('items',$where);

$b = new boardiClass();
?>
<!DOCTYPE html>
<head>
    <meta charset="UTF-8" />
    <meta name="robots" content="noindex,nofollow"/>
    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0, user-scalable=no"/>
    <meta http-equiv="X-UA Compatible" control="IE=edge,chrome=1" />
    <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" />
    <link rel="stylesheet" href="css/table.css" />
    <script src="http://code.jquery.com/jquery.min.js" ></script>
    <script src="bootstrap/js/bootstrap.min.js"></script>
    <script type="text/javascript" src="js/display.js"></script>
</head>
<body>
<main class="container-fluid">
    <div class="container">
        <div class="col-md-2 sidebar">
            <div class="row">

               <!-- 사이드 메뉴 영역 -->

            </div>
        </div>

        <!-- Main 화면 -->
        <div class="col-md-10 content">
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">
                        <div class="panel-body">
                            <?php require_once 'table-01.php';?>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</main>

</body>
</html>


=== table-01.php ===

<table class="table table-hover">
    <thead>
        <tr>
            <th class="header" width="30"><input type="checkbox" id="checkall" /></th>
            <th class="header" width="100">No</th>
            <th class="header" width="250">아이템</th>
            <th class="header" width="250">가격</th>
            <th class="header" width="200">수량</th>
        </tr>
    </thead>
    <tbody>
        <?php
            // 테이블 리스트
            $b->tablelistView_checkbox($result);
        ?>
    </tbody>                                       
    <td colspan="5" style="text-align:left;">
    <button type="button" class="btn btn-success btn-sm" id="optdel">선택삭제</button>
    <button type="button" class="btn btn-success btn-sm" id="optmp3">선택듣기</button>
    </td>
</table>

<a class="btn btn-default pull-right">글쓰기</a>

<?php $b->PageLinkView($link_url,$totalcnt,$rowsPage,$curPage);?>


// column 개수, 게시물 총개수 만큼 자동으로 화면 출력
function tablelistView_checkbox($result){
    global $dbconn;
    while($row = mysqli_fetch_array($result, MYSQLI_ASSOC)) {
        $view='<tr class="tr1">';
        foreach($row as $column => $value) {
            if($column== 'uid'){
                $view.='<td><input type="checkbox" class="chkbox" name="uid[]" value="'.$value.'" /></td>';
            }
            $view.='<td class="td2">'.$value.'</td>';
        }
        $view.='</tr>';
        echo $view;
    }
}

 function PageLinkView($link_url,$totalcnt,$rowsPage,$curPage,$m){
    echo '<div style="position:relative;vertical-align:top;padding-top:0;margin-top:0">';
        echo "<span style='position:absolute;top:10px;'>[전체 글수:".$totalcnt."]</span>";
        echo '<div class="text-center">';
        echo '<ul class="pagination">';
            $Info = $this->PageList($totalcnt,$rowsPage,$curPage,'');
            if($Info['current_block'] > 2){
                echo "<li><a href='".$link_url."?m=$m&p=1'>◀</a></li> ";
            }
            if($Info['current_block'] > 1){
                echo "<li><a href='".$link_url."?m=$m&p=".$Info['prev']."'>◁</a></li> ";
            }
            foreach($Info['current'] as $w) {
                if($curPage == $w){
                    echo "<li><a href='".$link_url."?m=$m&p=".$w."'><span style='color:red;'>".$w."</span></a></li> ";
                } else {
                    echo "<li><a href='".$link_url."?m=$m&p=".$w."'>".$w."</a></li> ";
                }
            }
            if($Info['current_block'] < ($Info['total_block'])){
                echo "<li><a href='".$link_url."?m=$m&p=".$Info['next']."'>▷</a></li> ";
            }
            if($Info['current_block'] < ($Info['total_block']-1)){
                echo "<li><a href='".$link_url."?m=$m&p=".$Info['totalPage']."'>▶</a></li> ";
            }
        echo '</ul>';
        echo '</div>';
    echo '</div>';
}

// $curPage : 현재 페이지, $totalcnt : 총 게시물수
// $block_limit : 한 화면에 뿌려질 게시글 개수
function PageList($totalcnt,$rowsPage,$curPage,$block_limit) {
    $block_limit = $block_limit ? $block_limit : 10;  // 한 화면에 보여줄 개수 기본 10으로 설정

    // 총 페이지수 구하기
    $totalPage = ceil($totalcnt/$rowsPage);
    if($totalPage == 0) {
        ++$totalPage;
    }
    $total_block = ceil($totalPage / $block_limit); //전체 블록 갯수

    $curPage = $curPage ? $curPage : 1; // 현재 페이지

    // 현재 블럭 : 화면에 표시될 페이지 리스트
    $current_block=ceil($curPage/$block_limit);
    // 현재 블럭에서 시작페이지
    $fstPage = (((ceil($curPage/$block_limit)-1)*$block_limit)+1);
    // 현재 블럭에서 마지막 페이지
    $endPage = $fstPage + $block_limit -1;
    if($totalPage < $endPage) {
        $endPage = $totalPage;
    }

    // 시작 바로 전 페이지
    $prev_page = $fstPage - 1;
    // 마지막 다음 페이지
    $next_page = $endPage + 1;

    foreach(range($fstPage, $endPage) as $val) {
        $row[] = $val;
    }
    // 배열로 결과를 돌려준다.
    return array(
        'total_block' => $total_block,
        'current_block' => $current_block,
        'totalPage' => $totalPage,
        'fstPage' => $fstPage,
        'endPage' => $endPage,
        'prev' => $prev_page,
        'next' => $next_page,
        'current' => $row
    );
}


블로그 이미지

Link2Me

,
728x90

https://startbootstrap.com/template-overviews/sb-admin-2/ 사이트에서 무료 Layout 을 하나 받아서 분석을 해봤다.


전체적인 Layout 구조하에서 테이블 Layout 이 어떻게 배치되는지를 집중적으로 분석했다.

테이블 Layout 구글검색을 해도 실제 테이블 부분만 다루고 있더라.

전체 Layout 에서 배치되는 영역까지는 찾아보기가 쉽지 않다.


본문 영역에 테이블이 배치되는 구조는 아래와 같다.

<div id="page-wrapper">

<div class="row"></div> <!-- 하나의 테이블 처리 div -->
<div class="row"></div>
<div class="row"></div>
<div class="row"></div>

</div>


css 파일에는 PC와 스마트폰(768px 이하)로 구분 처리하고 있다.

css 파일을 본인이 작성하는 css 에 내용을 추가하거나 상황에 맞게 수정해서 사용하면 된다.

#page-wrapper {
  padding: 0 15px;
  min-height: 568px;
  background-color: white;
}
@media (min-width: 768px) {
  #page-wrapper {
    position: inherit;
    margin: 0 0 0 250px;
    padding: 0 30px;
    border-left: 1px solid #e7e7e7;
  }
}


  /* 세로 방향 타블렛부터 가로 방향 타블렛과 데스크탑까지 */
 @media (min-width: 768px) and (max-width: 979px) { ... }
 
 /* 가로 방향 스마트폰부터 세로 방향 타블렛까지 */
 @media (max-width: 767px) { ... }
 
 /* 가로 방향 스마트폰 이하 */
 @media (max-width: 480px) { ... }


The Bootstrap grid system has four classes:
 - xs (for phones)
 - sm (for tablets)
 - md (for desktops)
 - lg (for larger desktops)
 


<div class="col-lg-12"> 를 적용하면 PC화면에 하나의 테이블이 화면 전체를 채운다.


<div class="row">
    <div class="col-lg-12">
        <div class="panel panel-default">
            <div class="panel-heading">
                테이블 제목 1
            </div>
            <div class="panel-body">
                <table width="100%" class="table table-striped table-hover" id="tables-example">
                    <thead>
                        <tr>
                            <th>#</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>1</td>
                        </tr>
                    </tbody>
                </table>
            </div><!-- .panel-body -->
        </div><!-- .panel -->
    </div><!-- .col-lg-12 -->
</div><!-- .row -->


만약 공지사항이나 메인화면처럼 화면에 여러개의 테이블을 표시하고 싶은 경우, 즉 왼쪽과 오른쪽에 두개의 테이블을 배치하고 싶은 경우에는 어떻께 할까?

<div class="col-lg-12"> 의 절반인 <div class="col-lg-6"> 를 두개 표시해준다.

그러면 PC화면에서는 2개의 테이블이 나란히 보이고 스마트폰에서는 자동으로 한테이블씩 나열해서 보여준다.


<div class="row">
    <div class="col-lg-6">
        <div class="panel panel-default">
            <div class="panel-heading">
                테이블 제목 2
            </div>
            <div class="panel-body">
                <table width="100%" class="table table-striped table-hover" id="tables-example">
                    <thead>
                        <tr>
                            <th>#</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>1</td>
                        </tr>
                    </tbody>
                </table>
            </div><!-- .panel-body -->
        </div><!-- .panel -->
    </div><!-- .col-lg-6 -->
</div><!-- .row -->


<div class="row">
    <div class="col-lg-6">
        <div class="panel panel-default">
            <div class="panel-heading">
                테이블 제목 3
            </div>
            <div class="panel-body">
                <table width="100%" class="table table-striped table-hover" id="tables-example">
                    <thead>
                        <tr>
                            <th>#</th>
                        </tr>
                    </thead>
                    <tbody>
                        <tr>
                            <td>1</td>
                        </tr>
                    </tbody>
                </table>
            </div><!-- .panel-body -->
        </div><!-- .panel -->
    </div><!-- .col-lg-6 -->
</div><!-- .row -->


이제 기존에 어설프게 table layout 처리했던 걸 전체 Layout 상에서 움직이도록 수정해야겠다.

기존에 작성 연습했던 topmenu.php Layout 과 조합해서 구조를 그려보자.

sidebar 는 2, content 는 10 (bootstrap grid system 은 12개의 화면 분할 구조로 처리)


<main class="container-fluid">
    <div class="container main-container">
        <div class="col-md-2 sidebar">
            <div class="row">
            </div>
        </div>

        <!-- Main 화면 -->
        <div class="col-md-10 content">
            <div class="row">
                <div class="col-md-12">
                    <div class="panel panel-default">

                        <div class="panel-heading">

                           <!-- 제목 적어주기 -->

                        </div>

                        <div class="panel-body">
                        </div>
                    </div>
                </div>
            </div>
        </div>
    </div>
</main>

블로그 이미지

Link2Me

,
728x90

부트스트랩을 적용하여 상단 메뉴에서 로그인, 로그아웃 처리하는 걸 구현중이다.

메뉴 Layout 이 완벽하게 마음에 들지 않는다. 좀 더 기능을 찾아보고 테스트해야 한다.

아직 미완성 부분도 있지만 현재까지 구현에 성공한 걸 적어둔다.


=== topmenu.php ===

<?php
if(!isset($_SESSION)) {
    session_start();
}
session_save_path('./_tmp/session');


// 단말 종류 검사 및 단말 구분에 따라 보여주는 화면 처리
require_once "deviceChk.php";


$titleName = "테스트";
?>

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="robots" content="noindex,nofollow"/>
    <?php if($device_type != 3):?>
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1" />
    <meta http-equiv="cache-control" content="no-cache" />
    <meta http-equiv="expires" content="0" />
    <meta http-equiv="pragma" content="no-cache" />
    <meta name="apple-mobile-web-app-capable" content="no" />
    <meta name="apple-mobile-web-app-status-bar-style" content="black" />
    <?php endif?>
    <title><?=$titleName;?></title>
    <link rel="stylesheet" href="css/topmenu.css" />
    <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css" />
    <script src="http://code.jquery.com/jquery.min.js" ></script>
    <script src="bootstrap/js/bootstrap.min.js"></script>
    <!--[if lt IE 9]> <!-- 5. 인터넷익스플로러 9버전 이하일 경우 html5가 인식될 수 있게 해주는 스크립트 -->
    <script src="//html5shiv.googlecode.com/svn/trunk/html5.js"></script>
    <script src="bootstrap/js/respond.min.js"></script>
    <script src="js/login.js"></script>
    <script type="text/javascript">
        var currentName = null;
        $(document).ready(function(){
            tabSetting(); // 탭 초기화 및 설정
        });

        function tabSetting() {
            $('.tabPage').hide();
            $('#navbar-collapse-2 a').on('click', function() {
                var tabName = $(this).attr('href');
                if(currentName != tabName){
                    $('.tabPage').hide();
                    currentName = tabName;
                }
                if($(tabName).is(":visible")){
                    //$(tabName).slideUp();
                    $(tabName).toggle();
                }else{                   
                    $(tabName).slideDown();
                }

                if(tabName != '#nav-loginForm'){ // 로그인 창이 아니면
                    // 반응형으로 메뉴 동작시 메뉴 클릭후 자동 숨김
                    $('#navbar-collapse-2').collapse('hide');
                }
            });
           
            $('.side-menu-container a').on('click', function() {
                var tabName = $(this).attr('href');
                if(currentName != tabName){
                    $('.tabPage').hide();
                    currentName = tabName;
                }
                if($(tabName).is(":visible")){
                    //$(tabName).slideUp();
                    $(tabName).toggle();
                }else{                   
                    $(tabName).slideDown();
                }
            });
        }

    </script>
</head>
<body>

<nav class="navbar navbar-default">
     <div class="container-fluid"><!-- 전체 화면을 사용 / container 클래스 선택자는 최대 넓이가 1170px -->
        <div class="navbar-header">
            <!--모바일 시 메뉴 숨기기 시작 -->
            <button type="button" class="navbar-toggle collapsed" data-toggle="collapse" data-target="#navbar-collapse-2">
                <span class="sr-only">Toggle navigation</span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
                <span class="icon-bar"></span>
            </button>
            <!--모바일 시 메뉴 숨기기 끝  -->
            <a class="navbar-brand" href="#"><?=$titleName;?></a>
        </div>
        <div class="collapse navbar-collapse" id="navbar-collapse-2">
            <ul class="nav navbar-nav navbar-left">
                <li><a href="#home">Home</a></li>
                <li><a href="#tab01">News</a></li>
                <li><a href="#tab02">Contact</a></li>
                <li><a href="#tab03">About</a></li>
                <li><a href="#tab04">Support</a></li>
                <li><a href="#tab05">Blog</a></li>
                <li><a href="#tab06">게시판</a></li>
                <li><a href="#base">Base</a></li>
                <li><a href="#custom">Custom</a></li>
                <li><a href="#more">More</a></li>
                <li><a href="#logo">Logo</a></li>
                <?php if(isset($_SESSION['userid']) && !empty($_SESSION['userid'])):?>
                    <li><a href="#" onclick="document.getElementById('logout-form').submit();">로그아웃</a></li>
                <?php else:?>
                    <li><a href="#nav-loginForm" data-toggle="modal" >로그인</a></li>
                <?php endif;?>
            </ul>

            <div class="modal fade" id="nav-loginForm" role="dialog">
                <div class="modal-dialog">
                    <div class="modal-content">
                        <div class="modal-header">
                            <button type="button" class="close" data-dismiss="modal">&times;</button>
                            <h4 class="modal-title"><span class="glyphicon glyphicon-lock"></span>로그인 폼</h4>
                        </div>
                        <div class="modal-body">
                            <form role="form" id="login-form">
                                <div class="form-group">
                                    <label for="userid"><span class="glyphicon glyphicon-user"></span>userID</label>
                                    <input type="text" class="form-control" id="userid" placeholder="아이디를 입력하세요" autofocus required />
                                </div>
                                <div class="form-group">
                                    <label for="password"><span class="glyphicon glyphicon-eye-open"></span>비밀번호</label>
                                    <input type="password" class="form-control" id="password" placeholder="비밀번호를 입력하세요" required />
                                </div>
                                <div class="checkbox">
                                    <label><input type="checkbox" value="" checked>로그인 유지</label>
                                </div>
                                <button type="submit" id="login-submit" class="btn btn-success btn-block"><span class="glyphicon glyphicon-off"></span>로그인</button>
                            </form>
                        </div>
                        <div class="modal-footer">
                            <button id="login_lost_btn" type="button" class="btn btn-link">비밀번호 찾기</button>
                            <button id="login_register_btn" type="button" class="btn btn-link">회원가입</button>
                            <!--<button type="button" class="btn btn-default" data-dismiss="modal">Close</button>-->
                        </div>
                        <!-- End # Login Form -->

                        <form id="logout-form" style="display:none" action="oauth/logout.php" method="POST"></form>

                        <!-- Begin | Lost Password Form --><!-- 좀 더 테스트 해봐야 할 영역 -->
                        <form id="lost-form" style="display:none;">
                            <div class="modal-body">
                                <div id="div-lost-msg">
                                    <div id="icon-lost-msg" class="glyphicon glyphicon-chevron-right"></div>
                                    <span id="text-lost-msg">Type your e-mail.</span>
                                </div>
                                <input id="lost_email" class="form-control" type="text" placeholder="E-Mail (type ERROR for error effect)" required>
                            </div>
                            <div class="modal-footer">
                                <div>
                                    <button type="submit" class="btn btn-primary btn-lg btn-block">Send</button>
                                </div>
                                <div>
                                    <button id="lost_login_btn" type="button" class="btn btn-link">Log In</button>
                                    <button id="lost_register_btn" type="button" class="btn btn-link">Register</button>
                                </div>
                            </div>
                        </form>
                        <!-- End | Lost Password Form -->

                        <!-- Begin | Register Form -->
                        <form id="register-form" style="display:none;">
                            <div class="modal-body">
                                <div id="div-register-msg">
                                    <div id="icon-register-msg" class="glyphicon glyphicon-chevron-right"></div>
                                    <span id="text-register-msg">Register an account.</span>
                                </div>
                                <input id="register_username" class="form-control" type="text" placeholder="Username (type ERROR for error effect)" required>
                                <input id="register_email" class="form-control" type="text" placeholder="E-Mail" required>
                                <input id="register_password" class="form-control" type="password" placeholder="Password" required>
                            </div>
                            <div class="modal-footer">
                                <div>
                                    <button type="submit" class="btn btn-primary btn-lg btn-block">Register</button>
                                </div>
                                <div>
                                    <button id="register_login_btn" type="button" class="btn btn-link">Log In</button>
                                    <button id="register_lost_btn" type="button" class="btn btn-link">Lost Password?</button>
                                </div>
                            </div>
                        </form>
                        <!-- End | Register Form -->

                    </div>

                </div>
            </div>
        </div><!-- .navbar-collapse -->
     </div><!-- .container-fluid -->
</nav>

<main class="container-fluid">
     <div class="container main-container">
         <div class="col-md-2 sidebar">
             <div class="row">
                 <div class="absolute-wrapper"> </div>
                 <!-- Menu -->
                 <div class="side-menu">
                     <nav class="navbar navbar-default" role="navigation">
                         <!-- Main Menu -->
                         <div class="side-menu-container">
                             <ul class="nav navbar-nav">
                                 <li><a href="#tab01"><span class="glyphicon glyphicon-cloud"></span> Link</a></li>
                             </ul>
                         </div>
                     </nav>
                 </div>
             </div>
            
         </div>
        
         <!-- Main 화면 -->            
         <div class="col-md-10 content">
             <div class="panel panel-default">
                 <div class="panel-body">
                    메인 본문 표시 영역
                    <div id="tab01" class="tabPage">
                    <img class="img-responsive" src="./images/img_01.jpg">
                    </div>
                    <div id="tab02" class="tabPage">
                        <img class="img-responsive" src="./images/img_02.jpg">
                    </div>
                    <div id="tab03" class="tabPage">
                        <img class="img-responsive" src="./images/img_03.jpg">
                    </div>
                    <div id="tab04" class="tabPage">
                        <img class="img-responsive" src="./images/img_04.jpg">
                    </div>
                    <div id="tab05" class="tabPage">
                        <img class="img-responsive" src="./images/img_05.jpg">
                    </div>
                 </div> 
            </div>   
         </div>    
    </div>
</main>

<footer class="pull-left footer">
    <p class="col-md-12">
        <hr class="divider">
        &copy; Linke2ME 2016
    </p>
</footer>

</body>
</html>


=== login.js ===

$(function(){
    $('#login-submit').click(function(){
        $('#login-form').submit(function(e){
            e.preventDefault();
            $.ajax({
                url: 'oauth/loginChk.php',
                type: 'POST',
                data: {userid:$('#userid').val(), password:$('#password').val()},
                dataType: "json",
                success: function (response) {
                if(response.result == 1){
                    //alert('로그인 성공');                       
                    $('#nav-loginForm').modal('hide');
                    location.reload(); // 화면 갱신                      
                } else {
                    alert('로그인 실패');
                }                   
                },
                error: function(){
                    alert('ajax form 전송에 문제가 있습니다');
                }
            });           
        });
    });

});


=== loginChk.php ===

<?php
if(!isset($_SESSION)) {
    session_start();
}

@extract($_POST);
if(isset($userid) && !empty($userid) && isset($password) && !empty($password)) {
    require_once 'dbconnect.php'; // db접속 성공
    require_once '../phpclass/loginClass.php';
   
    $salt = "&
c4f96fa549b58356d638%#!s29e5dce852e5d1";
    $password = hash('sha384', $password.$salt);

    $c=new LoginClass();
    $rs = $c->WebUserAuthCheck($userid,$password);
    if($rs == '1') {
        $_SESSION['userid'] = $userid;
        echo '{"result":"1"}';
    } else {
        echo '{"result":"0"}';
    }
}
?>


=== dbconnect.php ===

<?php
include_once 'dbinfo.php';
$dbconn = isConnectDb($db);

function isConnectDb($db)
{
    $conn = mysqli_connect($db['host'],$db['user'],$db['pass'],$db['name'],$db['port']);
    mysqli_set_charset($conn, "utf8");  // DB설정이 잘못되어 euc-kr 로 되어 있으면 문제가 됨
    if (mysqli_connect_errno()) {
        echo "Failed to connect to MySQL: " . mysqli_connect_error();
        exit;
    } else {
        return $conn;
    }
}
?>


=== logout.php ===

<?php
if(!isset($_SESSION)) {
    session_start();
}
unset($_SESSION['userid']);
if(session_destroy()){
    echo '{"result":"1"}';
    header("Location: ../topmenu.php");
}
?>


=== topmenu.css ===

@charset "utf-8";
@import url(http://fonts.googleapis.com/earlyaccess/nanumgothic.css);

body {padding-top: 0px;}

/* 로그인 폼 */
.modal-header {
      color:black !important;
      text-align: center;
      font-size: 30px;
}
.modal-footer {
      background-color: #f9f9f9;
      padding: 5px 5px 5px 5px;
      border-top: 0px;
}
/* 로그인 폼 End */ 

.sidebar { padding-left: 0;}
.main-container { background: #FFF; padding-top: 15px; margin-top: -20px;}
.footer { width: 100%;}   
:focus { outline: none;}
.side-menu {
    position: relative;
    width: 100%;
    height: 100%;
    background-color: #f8f8f8;
    border-right: 1px solid #e7e7e7;
}
.side-menu .navbar {
    border: none;
}
.side-menu .navbar-header {
    width: 100%;
    border-bottom: 1px solid #e7e7e7;
}
.side-menu .navbar-nav .active a {
    background-color: transparent;
    margin-right: -1px;
    border-right: 5px solid #e7e7e7;
}
.side-menu .navbar-nav li {
    display: block;
    width: 100%;
    border-bottom: 1px solid #e7e7e7;
}
.side-menu .navbar-nav li a {
    padding: 15px;
}
.side-menu .navbar-nav li a .glyphicon {
    padding-right: 10px;
}
.side-menu #dropdown {
    border: 0;
    margin-bottom: 0;
    border-radius: 0;
    background-color: transparent;
    box-shadow: none;
}
.side-menu #dropdown .caret {
    float: right;
    margin: 9px 5px 0;
}
.side-menu #dropdown .indicator {
    float: right;
}
.side-menu #dropdown > a {
    border-bottom: 1px solid #e7e7e7;
}
.side-menu #dropdown .panel-body {
    padding: 0;
    background-color: #f3f3f3;
}
.side-menu #dropdown .panel-body .navbar-nav {
    width: 100%;
}
.side-menu #dropdown .panel-body .navbar-nav li {
    padding-left: 15px;
    border-bottom: 1px solid #e7e7e7;
}
.side-menu #dropdown .panel-body .navbar-nav li:last-child {
    border-bottom: none;
}
.side-menu #dropdown .panel-body .panel > a {
    margin-left: -20px;
    padding-left: 35px;
}
.side-menu #dropdown .panel-body .panel-body {
    margin-left: -15px;
}
.side-menu #dropdown .panel-body .panel-body li {
    padding-left: 30px;
}
.side-menu #dropdown .panel-body .panel-body li:last-child {
    border-bottom: 1px solid #e7e7e7;
}
.side-menu #search-trigger {
    background-color: #f3f3f3;
    border: 0;
    border-radius: 0;
    position: absolute;
    top: 0;
    right: 0;
    padding: 15px 18px;
}
.side-menu .brand-name-wrapper {
    min-height: 50px;
}
.side-menu .brand-name-wrapper .navbar-brand {
    display: block;
}
.side-menu #search {
    position: relative;
    z-index: 1000;
}
.side-menu #search .panel-body {
    padding: 0;
}
.side-menu #search .panel-body .navbar-form {
    padding: 0;
    padding-right: 50px;
    width: 100%;
    margin: 0;
    position: relative;
    border-top: 1px solid #e7e7e7;
}
.side-menu #search .panel-body .navbar-form .form-group {
    width: 100%;
    position: relative;
}
.side-menu #search .panel-body .navbar-form input {
    border: 0;
    border-radius: 0;
    box-shadow: none;
    width: 100%;
    height: 50px;
}
.side-menu #search .panel-body .navbar-form .btn {
    position: absolute;
    right: 0;
    top: 0;
    border: 0;
    border-radius: 0;
    background-color: #f3f3f3;
    padding: 15px 18px;
}
/* Main body section */
.side-body {
    margin-left: 310px;
}

.navbar-brand { position: relative; z-index: 2; }

.navbar-nav.navbar-right .btn { position: relative; z-index: 2; padding: 4px 20px; margin: 10px auto; }

.navbar .navbar-collapse { position: relative; }
.navbar .navbar-collapse .navbar-right > li:last-child { padding-left: 22px; }

.navbar .nav-collapse { position: absolute; z-index: 1; top: 0; left: 0; right: 0; bottom: 0; margin: 0; padding-right: 120px; padding-left: 80px; width: 100%; }
.navbar.navbar-default .nav-collapse { background-color: #f8f8f8; }
.navbar.navbar-inverse .nav-collapse { background-color: #222; }
.navbar .nav-collapse .navbar-form { border-width: 0; box-shadow: none; }
.nav-collapse>li { float: right; }

.btn.btn-circle { border-radius: 50px; }
.btn.btn-outline { background-color: transparent; }

/* 화면 해상도에 따른 글자 크기 변경 */
@media only screen and (max-width: 768px) {
    .navbar .navbar-collapse .navbar-right > li:last-child { padding-left: 15px; padding-right: 15px; }
   
    .navbar .nav-collapse { margin: 7.5px auto; padding: 0; }
    .navbar .nav-collapse .navbar-form { margin: 0; }
    .nav-collapse>li { float: none; }
       
    .side-menu {
        position: relative;
        width: 100%;
        height: 0;
        border-right: 0;
    }

    .side-menu .navbar {
        z-index: 999;
        position: relative;
        height: 0;
        min-height: 0;
        background-color:none !important;
        border-color: none !important;
    }
    .side-menu .brand-name-wrapper .navbar-brand {
        display: inline-block;
    }
    /* Slide in animation */
    @-moz-keyframes slidein {
        0% {
            left: -300px;
        }
        100% {
            left: 10px;
        }
    }
    @-webkit-keyframes slidein {
        0% {
            left: -300px;
        }
        100% {
            left: 10px;
        }
    }
    @keyframes slidein {
        0% {
            left: -300px;
        }
        100% {
            left: 10px;
        }
    }
    @-moz-keyframes slideout {
        0% {
            left: 0;
        }
        100% {
            left: -300px;
        }
    }
    @-webkit-keyframes slideout {
        0% {
            left: 0;
        }
        100% {
            left: -300px;
        }
    }
    @keyframes slideout {
        0% {
            left: 0;
        }
        100% {
            left: -300px;
        }
    }
    /* Slide side menu*/
    /* Add .absolute-wrapper.slide-in for scrollable menu -> see top comment */
    .side-menu-container > .navbar-nav.slide-in {
        -moz-animation: slidein 300ms forwards;
        -o-animation: slidein 300ms forwards;
        -webkit-animation: slidein 300ms forwards;
        animation: slidein 300ms forwards;
        -webkit-transform-style: preserve-3d;
        transform-style: preserve-3d;
    }
    .side-menu-container > .navbar-nav {
        /* Add position:absolute for scrollable menu -> see top comment */
        position: fixed;
        left: -300px;
        width: 300px;
        top: 43px;
        height: 100%;
        border-right: 1px solid #e7e7e7;
        background-color: #f8f8f8;
        overflow: auto;
        -moz-animation: slideout 300ms forwards;
        -o-animation: slideout 300ms forwards;
        -webkit-animation: slideout 300ms forwards;
        animation: slideout 300ms forwards;
        -webkit-transform-style: preserve-3d;
        transform-style: preserve-3d;
    }
    @-moz-keyframes bodyslidein {
        0% {
            left: 0;
        }
        100% {
            left: 300px;
        }
    }
    @-webkit-keyframes bodyslidein {
        0% {
            left: 0;
        }
        100% {
            left: 300px;
        }
    }
    @keyframes bodyslidein {
        0% {
            left: 0;
        }
        100% {
            left: 300px;
        }
    }
    @-moz-keyframes bodyslideout {
        0% {
            left: 300px;
        }
        100% {
            left: 0;
        }
    }
    @-webkit-keyframes bodyslideout {
        0% {
            left: 300px;
        }
        100% {
            left: 0;
        }
    }
    @keyframes bodyslideout {
        0% {
            left: 300px;
        }
        100% {
            left: 0;
        }
    }
    /* Slide side body*/
    .side-body {
        margin-left: 5px;
        margin-top: 70px;
        position: relative;
        -moz-animation: bodyslideout 300ms forwards;
        -o-animation: bodyslideout 300ms forwards;
        -webkit-animation: bodyslideout 300ms forwards;
        animation: bodyslideout 300ms forwards;
        -webkit-transform-style: preserve-3d;
        transform-style: preserve-3d;
    }
    .body-slide-in {
        -moz-animation: bodyslidein 300ms forwards;
        -o-animation: bodyslidein 300ms forwards;
        -webkit-animation: bodyslidein 300ms forwards;
        animation: bodyslidein 300ms forwards;
        -webkit-transform-style: preserve-3d;
        transform-style: preserve-3d;
    }
    /* Hamburger */
    .navbar-toggle-sidebar {
        border: 0;
        float: left;
        padding: 18px;
        margin: 0;
        border-radius: 0;
        background-color: #f3f3f3;
    }
    /* Search */
    #search .panel-body .navbar-form {
        border-bottom: 0;
    }
    #search .panel-body .navbar-form .form-group {
        margin: 0;
    }
    .side-menu .navbar-header {
        /* this is probably redundant */
        position: fixed;
        z-index: 3;
        background-color: #f8f8f8;
    }
    /* Dropdown tweek */
    #dropdown .panel-body .navbar-nav {
        margin: 0;
    }    
}

블로그 이미지

Link2Me

,