페이지

2018년 7월 25일 수요일

JavaScript의 encodeURIComponent 함수를 이용하여 인코딩 디코딩하기






HTTP로 URL 값을 전달할때 오로지 영문자와 숫자만으로 전달한다면 인코딩 디코딩이 필요 없을 것이나 실제에 있어서는 다양한 특수문자와 한글 등이 URL 값에 포함되어 전달되는데 이때 제대로 인식을 못해서 404 Not found 에러가 발생하거나 잘못된 값이 전달되는 경우가 발생할수 있다.
예를 들어 

http://www.xxx.xx/info?page=3&title=developer&enginner

이 경우 title에 developer&engineer 값이 전달되어야 겠지만 &라는 특수문자는 &문자 자체로 인식되지 않고 특수한 기능을 하는 문자로 취급되기 때문에 문제가 발생한다.

이 문제를 해결하기 위해 title의 데이터 부분만(developer&enginner) 따로 인코딩해서 보낸다면 원하던 목적을 달성할수가 있게된다.
이때 사용하는 JavaScript의 함수가 encodeURIComponent()와 decodeURIComponent()가 있다.

encodeURIComponent()함수는 영 대문자와 소문자, 숫자, 그리고 *-_.을 제외한 모든 문자를 유니코드 형식으로 변환한다.
1BYTE 문자는 %XX, 2BYTE 문자는 %uXXXX 형태로...

그리고 이렇게 인코딩된 문자열을 사람이 읽을수 있는 형태로 원복할려면 decodeURIComponent()를 이용하면 된다.

아래는 예제코드이다.

<!DOCTYPE html>
<html>
<body>

<p>Click the button to encode a URI and for decoding also.</p>

<button onclick="myFunction()">Now Try it</button><br/><br/>

<hr/>
"developer&enginner"이라는 문자열을 encodeURIComponent()와 decodeURIComponent()를 이용해서 인코딩과 디코딩을 테스트해 봅니다.
<hr/>

<p/>인코딩 : <span  id="enco"></span>
<p/>디코딩 : <span id="deco"></span>
<P/>최종결과 : <span id="result"></span>

<script>
function myFunction() {
    var mURL = "http://www.xxx.xx/info?page=3&title=";
    var uri = "developer&enginner";
    var res = encodeURIComponent(uri);
    document.getElementById("enco").innerHTML = res;
    
    var dc = decodeURIComponent(res);
    document.getElementById("deco").innerHTML = dc;
    //alert("decoded : " + dc);
    
    var rt = mURL + res;
    document.getElementById("result").innerHTML = rt;
}
</script>

</body>
</html>

한글의 경우도 인코딩해서 보내고 받는 쪽에서 디코딩해서 처리하면 문제가 없을 것이다.
위 코드의 실행결과는 다음과 같이 나오게 될 것이다.

인코딩 : developer%26enginner

디코딩 : developer&enginner

최종결과 : http://www.xxx.xx/info?page=3&title=developer%26enginner

jQuery를 이용해서 select 태그의 선택된(selected) 항목의 값 가져오기.






jQuery를 이용해서 콤보 박스 혹은 full-down 메뉴인 select 태그의 선택된(selected) 항목의 값 가져오기.

2가지 방법으로 처리해 보고자 한다.
첫 번째는 select 태그를 이용해서 값을 가져오는 방법과 두 번째는 select의 id를 이용해서 값을 가져오는 방법이다.
자세한 방법은 아래 코드에서...

<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
<script src="https://code.jquery.com/jquery-2.2.4.min.js"></script>
</head>
<body>
<select name="name_Select" id="id_Select">
<option value="val1">txtVal1</option>
<option value="val2">txtVal2</option>
<option value="val3">txtVal3</option>
<option value="val4">txtVal4</option>
</select>
<hr/>
<button id="valFunc_tag">val 함수 using tag name</button><br/>
<button id="textFunc_tag">text 함수 using tag name</button>
<hr/>
<button id="valFunc_id">val 함수 using id</button><br/>
<button id="textFunc_id">text 함수 using id</button>
<script>
$(document).ready(function(){
$("#valFunc_tag").on("click", function(){
//select라는 태그 이름을 이용해서 select 박스를 선택한 후
// .val()함수로 값을 가져오면 option의 value 값을 가져온다. 
//따라서 val1, val2, val3...의 값을 가져온다.
var kkk = $("select option:selected").val();
alert(kkk);
});

$("#textFunc_tag").on("click", function(){
//select라는 태그 이름을 이용해서 select 박스를 선택한 후
// .text()함수로 값을 가져오면 option의 text 값을 가져온다. 
//따라서 txtVal1, txtVal2, txtVal3...의 값을 가져온다.
var kkk = $("select option:selected").text();
alert(kkk);
});
$("#valFunc_id").on("click", function(){
//아래의 경우는 select 박스의 id를 이용해서 select 박스를 선택하되
//나머지는 위의 설명과 같다.
var kkk = $("#id_Select option:selected").val();
alert(kkk);
});

$("#textFunc_id").on("click", function(){
var kkk = $("#id_Select option:selected").text();
alert(kkk);
});
});
</script>
</body>
</html>

2018년 7월 23일 월요일

Spring MVC의 @ModelAttribute 어노테이션에 대한 개념 정리







Spring MVC의 @ModelAttribute 어노테이션에 대한 개념 정리

Spring MVC에서 @ModelAttribute을 메소드의 파라미터로 사용할 경우 프로그램이 어떤 식으로 돌아가는지를 정리하고자 한다.
다른 어노테이션에 비해 @ModelAttribute는 내부적으로 돌아가는 부분이 많은 것 같다. 즉 Spring framework이 내부에서 알아서 처리해 주는 부분이 다른 어노테이션에 비해 더  많은 것 같다. 따라서 개발자의 손을 떠나 보이지 않는 가운데서 처리되는 부분에 대한 개념이 없다면 어둠 속에서 더듬이가 될수 밖에 없는 것이다.

여기 다음과 같은 빈 클래스가 있다고 할때

public class MemberInfo 
{
private int seq;
private String name;
private int age;

//이하 getter, setter는 생략
}

http://localhost:8080/member/info?name=Gildong&age=25seq=327

와 같이 접속되어 들어올 때 Controller 클래스의 아래 메소드가 실행될 것이다.

@Controller
@RequestMapping("/member/*")
public class MemberController
{
  ... 중 략 ...
@RequestMapping(value = "/info", method=RequestMethod.GET)
public void show(@RequestParam("seq") int seq, @ModelAttribute("myMEM") MemberInfo info, Model model)
{
logger.info("####### info.getName() "+info.getName());
logger.info("####### info.getAge() "+info.getAge());

try {
//service.read(seq)가 MemberVO 객체를 반환한다고 할 경우
model.addAttribute(service.read(seq));
}catch(Exception e) {
e.printStackTrace();
}
}
  ... 후 략 ...
}

이 메소드가 실행되면 info.jsp가 자동으로 실행되게 된다. 따라서 info.jsp가 만들어져 있어야 되고 없으면 404 Not found 에러가 발생할 것이다.
그리고 info.jsp의 아래 코드에서 결과가 나오게 될 것이다.

<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
<meta charset="EUC-KR">
<title>Insert title here</title>
</head>
<body>
<h3>회원 이름(info.name) : ${info.name }</h3> <%-- 여기서는 아무것도 안 나옴 --%>
<hr/>
<h3>회원 이름(myMEM.getName()) : ${myMEM.getName() }</h3>   <%-- 회원 이름이 정상적으로 출력된다. --%>
<h3>현재 이름(myMEM.name) : ${myMEM.name }</h3>             <%-- 회원 이름이 정상적으로 출력된다 --%>
<hr/>
<h3>회원 번호 : ${memberVO.seq }</h3>    <%-- 회원 번호가 정상적으로 출력된다 --%>
</body>
</html>

JSP의 코드는 위의 내용이 전부이다. 무엇이 어떻게 돌아가기에 http url로 들어온 회원 이름과 회원 번호가 Controller에 저장이되고 Controller에서 JSP로 특별하게 보내는 코드도 없어 보이는데 JSP 코드에서 저렇게 값이 정상적으로 출력이 된단 말인가?
이것이 @ModelAttribute의 위력이고 편리함이면서 동시에 개발자를 더듬이로 만드는 측면이기도 하다.
단순한 @ModelAttribute가 무슨 역할을 했단 말인가?

@ModelAttribute 선언 후 자동으로 진행되는 작업들은 다음과 같다.
     ① @ModelAttribute 어노테이션이 붙은 객체를 자동으로 생성한다. 
         위의 코드에서는 MemberInfo 클래스의 객체 info를 자동으로 생성한다. 
         이때 @ModelAttribute가 지정되는 클래스는 빈 클래스라야 한다. 

         즉 MemberInfo 클래스는 beans 클래스라야 한다.
         그리고 getter와 setter가 명명 규칙에 맞게 만들어져 있어야 한다.

   ② 생성된 오브젝트에(info) HTTP로 넘어 온 값들을 자동으로 바인딩한다. 
       위의 코드의 경우는 name=Gildong&age=25seq=327 이렇게 들어오는 
       name, age, seq의 값이 MemberInfo의 해당 변수의 setter를 통해서 
       해당 멤버 변수에로 binding된다.

   ③ @ModelAttribute 어노테이션이 붙은 객체가(여기서는 MemberInfo 객체) 
       자동으로 Model 객체에 추가되고 따라서 MemberInfo 객체가 .jsp 뷰단까지 전달이 된다.

이상의 작업이 개발자를 대신해서 Spring framework가 알아서 다 처리해 준다. 편리하긴하다. 
이때 @ModelAttribute() 괄호 안에 지정한 문자열(위의 경우에는 myMEM)의 의미를 알아야 한다. 이 문자열의 이름으로(이것이 객체이다) Model 객체에 자동으로 추가가 되고 따라서 JSP 뷰단으로 안전하게 넘어가게 된다. 즉 MemberInfo 객체가 Model 객체에 추가될 때 @ModelAttribute()의 괄호 안에 지정한 문자열의 이름으로 추가된다는 점이다. 그리고 이 문자열 이름은 MemberInfo의 객체인 것이다. 
만일 @ModelAttribute()의 괄호 안에 아무런 문자열도 지정하지 않으면 JSP 페이지에서 MemberInfo 객체에 저장되어 있는 값을 사용할수가 없게된다.

보이지 않는 가운데서 내부적으로 Spring에 의해 처리되는 이상의 작업들로 인해 info.jsp에서 다음 코드가 유효하게 동작하는 것이다.

<h3>회원 이름(myMEM.getName()) : ${myMEM.getName() }</h3>   <%-- 회원 이름이 정상적으로 출력된다. --%>
<h3>현재 페이지(myMEM.name) : ${myMEM.name }</h3>             <%-- 회원 이름이 정상적으로 출력된다 --%>

여기서 ${myMEM.getName()}과 ${myMEM.name}의 차이가 무엇인고 하면 전자의 경우는 MemberInfo의 메소드를 직접 호출해서 사용한 경우이고 후자의 경우는 MemberInfo의 멤버 변수 name을 JSTL에서 사용하면 자동으로 name의 getter인 getName()이 호출되게 되는 것이다.

2018년 7월 22일 일요일

jQuery attr() 함수에 대해






jQuery attr() 함수에 대해

jQuery의 attr() 함수는 선택된 특정 element의 값을 반환하거나 해당 element에 값을 설정하거나의 동작을 하는 함수이다.

$(selector).attr(attribute)
 ⇒ 해당 attribute의 값을 반환한다.

$(selector).attr(attribute, value)
 ⇒ 해당 attribute의 값을 value의 값으로 설정한다.

$(selector).attr({attribute:value, attribute:valeu, ...})
 ⇒ 여러개의 attribute에 값을 설정하기

아래는 예제코드이다.

<!DOCTYPE html>
<html>
<head>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

<script>
$(document).read(function(){
//$("button").click(function(){
$(".orgn").click(function(){
//img some.jpg의 width를 400으로 설정하기
$("img").attr("width", 400);
});

$(".joe").click(function(){
//img some.jpg의 width 값을 반환하기
alert("Image's width : " + $("img").attr("width"));
});
});
</script>
</head>
<body>

<img src="some.jpg" alt="Some Image" width="300" height="200"><br>

<button class="orgn">이미지 width 값 설정</button><br/>
<button class="joe">Image Width 보기</button>

</body>
</html>

JavaScript의 preventDefault() 메소드에 관해





JavaScript의 preventDefault() 메소드에 관해

HTML에서 a 태그는 기본적으로  href가 지정한 URL로 이동하게되어 있다. 그런데 기본적인 링크 동작을 다르게 변경하거나 추가할 뭔가가 있을 때 JavaScript에서 기본적인 연결을 해제하고 JavaScript 단에서 다른 원하는 동작을 하게 할수도 있다.

<!DOCTYPE html>
<html>
<body>
<hr/>
<a id="myBlog" href="http://developer-joe.tistory.com/">코드조각 블로그로 이동</a>
<p>JavaScript의 preventDefault() 함수는 위의 링크에 있는 url 연결 동작을 해제한다.</p>

<script>
document.getElementById("myBlog").addEventListener("click", function(event){
event.preventDefault();
});
</script>

</body>
</html>

2018년 7월 19일 목요일

Spring MVC에서의 동작의 흐름 간단 정리






※ 본 내용은 구멍가게 코딩단의 "코드로 배우는 스프링 웹 프로젝트"라는 책의 내용을 공부하다가 정리하게 된 내용이다.

Spring MVC의 동작 구조, 동작 흐름을 파악하는 것이 그렇게 간단하지가 않다.
게시판 관련 작업을 한다고 할때 다음과 같은 클래스들이 있다고 가정해 보자.

BoardDAO : interface로 MyBatis XML Mapper를 활용하여 DB 연동 작업
BoardDAOImpl : BoardDAO interface를 구현한 클래스
BoardController : @RequestMapping 어노테이션을 활용하여 HTTP URL과 jsp 뷰단을 mapping(연결)하는 역할

이상과 같을 때 게시물에 대한 페이징 처리를 위해 Criteria라는 클래스가 있다고 하자.

public class Criteria 
{
private int myPage;          //MySQL limit 구문에서 시작 페이지로 지정할 변수
private int perPageNum;   //한 페이지당 보여질 게시물 갯수

public int getMyPage() {  //myPage에 대한 getter
... 생 략 ...
}
public void setMyPage(int myPage) {  //myPage에 대한 setter
... 생 략 ...
}

... 이하 생략 ...
}

이 클래스는 전체 게시물을 하나의 페이지당 몇 개씩 보여줄 것인지의 정보와 전체 페이지들 중에서 몇 번째 페이지의 게시물을 가져올 것인지에 대한 정보를 관리하는 역할하는 클래스이다.
이를 때에 BoardController에 아래와 같은 페이징 처리하는 메소드가 있다고 가정하자.

@Controller
@RequestMapping("/board/*")
public class BoardController 
{
private static final Logger logger = LoggerFactory.getLogger(BoardController.class);

... 중략 ...

@RequestMapping(value = "/listCri", method=RequestMethod.GET)
public void listAll(Criteria cri, Model model) throws Exception
{
model.addAttribute("list", service.listCriteria(cri));
}

... 이하 생략 ...
}

이상의 상황에서 다음과 같은 url 접속이 있을 때 위의 각각의 클래스들이 어떻게 연결지어가면서 동작하는가 하는 것이다.
(아래에서 myPage와 perPageNum은 Criteria의 멤버 변수명과 일치해야 한다)

http://localhost:8080/board/listCri?myPage=3&perPageNum=15

와 같이 접속해 오면 @RequestMapping 어노테이션에 의해 BoardController의 다음 메소드가 실행이 될 것이다.

@RequestMapping(value = "/listCri", method=RequestMethod.GET)
public void listAll(Criteria cri, Model model) throws Exception
{
model.addAttribute("list", service.listCriteria(cri));
}

그리고 여기서 개발자의 손을 떠난 보이지 않는 영역가운데서 Spring에 의해서 모종의 동작들이 내부적으로 진행이 되는 것이다. 위의 listAll() 메소드에 개발자가 작성한 코드는 딸랑 한 줄 뿐이다. 이런 면이 편리하기도 하지만 내막을 모르면 개발자는 눈감고 더듬는 더듬이가 되는 것이다. 
위의 메소드에서 내부적으로 Spring 프레임웤이 자동으로 Criteria 클래스의 객체를 생성하면서 http url에 있는 myPage, perPageNum의 값을 각각 Criteria의 두 멤버 변수의 getter, setter를 이용해서 값을 할당하게 된다.
그런 후에 model을 이용해서 jsp 단에 해당 값을 넘기고 @RequestMapping의 value에 지정한 값인 listCri라는 이름의 jsp인 listCri.jsp를 실행한다(이것 또한 명시적으로 return "listCri"라고 하지 않았지만 또 역시 내부적으로 Spring에 의해처 처리가 되는 영역이다).

그런데 여기서 Criteria의 멤버 변수 perPageNum을 pgNum으로 변경할 경우 다음과 같이 접속을 하면 어떻게 될까?

http://localhost:8080/board/listCri?myPage=3&pgNum=15

만일 멤버 변수만 perPageNum을 pgNum으로 변경했다면 위의 접속은 15페이지 값이 적용되지 않는다.
원리상 되야 될것 같은데 정상동작하지 않는다.
해법은 perPageNum에 맞게 명명된 getter와 setter를 pgNum에 맞게 변경해 주어야 한다. 이것까지 변경해 주어야 위의 url은 비로소 정상 동작을 하는 것이다.
pgNum에 대한 getter, setter 명명 규칙대로 변경하면 getter는 getPgNum()이 될 것이고 setter는 setPgNum()이 될 것이다.
이렇게 getter, setter 명명 규칙에 맞게 메소드 명을 변경하지 않으면(대소문자도 정확히) 아래 URL은 정상 동작을 기대하지 말아야 한다.

http://localhost:8080/board/listCri?myPage=3&pgNum=15

순수 Java/JSP로 개발할 때는 개발자가 직접 코딩해야 하던 것들이 Spring을 이용하면 개발자의 손 가락을 쉬게해 주는 여러 편리한 면도 있지만 이 편리함이라는 게 개발자의 손을 떠난 영역에서 동작하는 것들이 점점 많아 지게 된다는 이야기가 되고 개발자는 더듬이가 되어가야 한다는 이야기로 귀결하는 것 같다.
그래도 아무튼 뭐 어쩌겠는가?

2018년 7월 17일 화요일

Spring에서의 @RequestMapping 어노테이션에 대한 간단 정리






Spring에서의 @RequestMapping 어노테이션에 대한 간단 정리

-. @RequestMapping 어노테이션은 Spring 웹 애플리케이션에서 가장 자주 사용되는 annotation이다.
-. @RequestMapping은 http request로 들어오는 url을 특정 controller 클래스나 메소드로 연결시키는 역할을 한다.
-. @RequestMapping은 controller에 있어서 class에 적용할수도 있고 특정 method에 적용할수도 있다.

아래의 예를 통해서 확인해 보자.

@Controller
@RequestMapping("/home")
public class TestController 
{
     //아래 @RequestMapping은 
     //hostname:port/home/에 대한 http request url에 대응하는 역할
    @RequestMapping("/")
    String getName(){
        return "Hello from getName() method";
    }

     //아래 @RequestMapping은 
     //hostname:port/home/info/에 대한 http request url에 대응하는 역할
    @RequestMapping("/info")
    String showInfo(){
        return "Hello from showInfo() method";
    }
}

-. http://localhost:8080/home/은 getName()을 호출하게 된다.
-. http://localhost:8080/home/info은 showInfo()을 호출하게 된다.

아래와 같이 @RequestMapping을 이용해서 특정한 method에 Multiple URI를 적용할수도 있다. 

@Controller
@RequestMapping("/home")
public class TestController 
{
    @RequestMapping(value={"", "/info", "info*","view/*,**/msg"})
    String myMultiMapping(){
        return "Hello from myMultiMapping()";
    }
}

위와 같이 @RequestMapping은 와일드 카드(wildcards)도 사용할수 있다. 
위의 코드에서 아래의 모든 URL들은 myMultiMapping() 메소드로 연결될 것이다.

localhost:8080/home
localhost:8080/home/
localhost:8080/home/info
localhost:8080/home/infosub
localhost:8080/home/view/
localhost:8080/home/view/view

2018년 7월 12일 목요일

외부의 텍스트 파일로 작성된 SQL문을 이용하여 MySQL의 table 만들기






외부의 텍스트 파일로 작성된 SQL문을 이용하여 MySQL의 table 만들기

d:\mydir\member.sql이라는 이름으로 다음과 같은 SQL문이 작성되어 있다고 할때 SQL문이 작성된 외부의 파일로부터 MySQL의 테이블 생성하는 방법이다.

create table tbl_member (
userid varchar(50) not null,
userpw varchar(50) not null,
username varchar(50) not null,
email varchar(100),
regdate timestamp default now(),
updatedate timestamp default now(),
primary key(userid)
);

먼저 mysql에 로그인한다. 만일 tbl_mamber라는 테이블을 생성할 데이터베이스가 book이라고 한다면
C:\>mysql -uroot -p

mysql>use book;
Database changed

mysql>source d:/mydir/member.sql
Query OK, 0 rows affected (0.03 sec)

여기서 중요한 것은 윈도우즈에서 경로 표시때 사용되는 \(backslash)를 사용하는 것이 아니라 /(forwardslash)를 사용한다는 점이다.
이상을 tbl_member라는 book이라는 데이터베이스에 tbl_member라는 테이블을 생성했다.

mysql> show tables;
+--------------------+
| Tables_in_book     |
+--------------------+
| tbl_member         |
+--------------------+
1 row in set (0.00 sec)

mysql> desc tbl_member;
+------------+--------------+------+-----+-------------------+-------+
| Field      | Type         | Null | Key | Default           | Extra |
+------------+--------------+------+-----+-------------------+-------+
| userid     | varchar(50)  | NO   | PRI | NULL              |       |
| userpw     | varchar(50)  | NO   |     | NULL              |       |
| username   | varchar(50)  | NO   |     | NULL              |       |
| email      | varchar(100) | YES  |     | NULL              |       |
| regdate    | timestamp    | NO   |     | CURRENT_TIMESTAMP |       |
| updatedate | timestamp    | NO   |     | CURRENT_TIMESTAMP |       |
+------------+--------------+------+-----+-------------------+-------+
6 rows in set (0.01 sec)

왜 interface인가, interface가 갖는 의의와 목적






왜 interface인가, interface가 갖는 의의와 목적

Java 등에서 interface가 갖는 의의는 interface를 "구현(implements)"하는 하위 클래스들 모두에 동일한 기능(메소드)를 갖도록 함을 통해서 동일한 사용자 경험을 갖도록 강제하는 의의를 가지고 있는 것이 interface이다.
이에 대한 자세한 이야기는 여기를 클릭

이와 함께 또 하나 interface가 갖는 의의는 전체적으로 어떤, 어떤 기능이 있어야 할 것인지 전체적인 그림을 그릴수 있도록 하는 것이 interface가 하는 역할이다.

inteface는 각각의 메소드의 몸체를 구현하지 않아도 되기에 전체적으로 있어야 할 기능들에 대한 메소드 이름만 지정하면서 필요한 기능들의 전체 그림을 그릴수 있도록 하는 역할을 하는 것이 interface이다.

이렇게 지정한 각 메소드들의 detail한 실제 기능구현은 interface를 "구현(implements)"하는 하위 클래스들에서 숙고하고 고려하면 되므로 interface는 전체 방향설정 역할 혹은 나아갈 방향에 대한 지도(map) 혁할을 한다는데 또 하나의 중요한 의의가 있다.

전체적인 숲의 모양을 그려주고 그 숲의 세부적인 부분들은 하위 클래스에서 구현하면 되도록 함을 통해서 즉 전체 그림과 세부적인 실제 기능을 분리함을 통해서 개발을 보다 용이하게 혹은 배가 산으로 가는 일이 없도록 하는 역할을 하는 것이 interface가 갖는 의의이고 목적이다.

2018년 7월 6일 금요일

통신사들이 막아 놓은 80 포트와 가정에서 웹 서버 구축 이야기





구형 노트북에 Linux로 Apache 웹 서버를 구축했다. 그리고 ipTIME의 DDNS로 도메인을 만들고 동영상 스트리밍 서버를 간단하게 만들었다.
그리고는 도메인으로 접속을 하니 잘 되었다. 
그런데 외부에 있는 사람에게 URL 주소를 주고 접속하게 했더니 .... 접속이 되지를 않았다.
말이 안되는 상황이 벌어졌다.

몰론 당연히 ipTIME에서 80 포트를 웹 서버로 사용하는 노트북으로 포트 포워딩을 했다.
결국 추적해 보니 사용하고 있는 통신사 LGU+에서 80 포트를 막아놓았다는 것을 알아냈다.
LGU+에 몇 차례에 걸쳐서 전화를 해 본 결과 실토를 받아냈다.

보안상의 이유 어쩌고 하는 말도 안되는 소리를 했고(80포트가 그렇게 보안상 막아야 한다면 기업 인터넷에서 왜 열어주는 것임?) 정책상 열어 줄수 없다는 최종적인 실토를 받아냈다.  통신사들의 갑질 아닌가 이게? 정상적으로 사용료를 지불하는데 어째서 80 포트를 사용 못해야한단 말인가?
이 과정에서 포트 개폐 유무를 체크해 주는 좋은 사이트를 발견했다.
아래 사이트는 포트 뿐만 아니라 IP 위치, Traceroute 등 다양한 유용한 기능들을 제공하는 쓸모가 많은 사이트이다.


결국 위의 문제는 세상 모든 사람들이 공통적으로 사용하는 80 포트를 사용하지 못하고 외부에서 들어오는 포트를 별다른 포트(예를들어 8181)로 만들고 그것을 내부에서는 80포트로 포트 포워딩 하는 작업을 해서 처리는 했지만 재미없는 일이다.




그래서 결국은 xxx.iptime.org:8181/xxx.php와 같은 방식으로 접속을 해야하는 수 밖에 없게 되었다.

포트포워딩 얘기가 나온김에 참고적으로 ipTIME 공유기의 DDNS를 이용해서 외부에서 puTTY를 이용해서 SSH로 접속이 가능할려면 SSH로 사용할 포트에 대해서도 역시 ipTIME 공유기의 관리자 페이지에서 포트포워딩해 주어야 된다.
FTP나 SFTP 등의 경우에 대해서도 역시 마찬가지 작업을 해 주어야 FileZilla나 puTTY를 통해 웹 서버에 접속이 가능하게 된다.
물론 Linux 서버단에서의 설정도 당연히 해 주어야 하는 건 그 또한 당연한 일이다.

짜증 나는 LGU+ 통신사로구나. 다른 통신사도 마찬가지인지 모르겠다. 80 포트 허용해 주는 통신사로 이동해야겠다.