2017년 5월 31일 수요일

C#, WebForm : 썸머노트(summernote) 에 이미지 업로드 기능 달기


이전에 썸머 노트를 사용해 보았다.

참고 : 위지윅 에디터 (Wysiwyg Editor) 썸머노트(summernote)

문서 작성 까지는 잘 되지만, 이미지를 삽입할때 문제가 된다.


툴 아이콘중 그림 표시를 클릭해보면....


일반적인 이미지 선택창이 나오고 여기서 이미지를 지정하면...


이렇게 이미지가 잘 들어간다.
하지만, 저기 "소스 보기" 아이콘을 클릭하여 html 소스를 보게 되면...


이렇게 일반적인 이미지 링크가 아닌, Base64 로 인코딩된 문자열이 이미지로 삽입되어 있는 것을 볼수 있다.
즉, 문서내에 이미지가 포함되어 있는 것.

이것도 나름 장점이 많은 방식이긴 하지만, 이렇게 하면 전체적으로 데이터가 많이 늘어나는데다, 이미지 관리도 안되고, 특히나 아주 큰 이미지가 첨부되었을 경우 아예 DB 에 넣지도 못하는 경우까지 생길수 있어 좀 골치 아프다.

이걸 일반적인 이미지 업로드해서 이미지 링크가 되게끔 바꿔 보자.
썸머노트에는 이것을 위해 콜백 함수를 지원한다.


현재 이렇게 되어 있을 텐데, 여기에 콜백 함수를 추가한다.


<%@ Page Language="C#" AutoEventWireup="true" CodeFile="BoardEntry.aspx.cs" Inherits="SummerNote_BoardEntry" %>

<!DOCTYPE html>

<html xmlns="http://www.w3.org/1999/xhtml">
<head runat="server">
    <title></title>
       
    <!-- include libraries(jQuery, bootstrap) -->
    <link href="http://netdna.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.css" rel="stylesheet">
    <script src="http://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.js"></script>
    <script src="http://netdna.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.js"></script>

    <!-- include summernote css/js-->
    <link href="dist/summernote.css" rel="stylesheet" type="text/css" />
    <script src="dist/summernote.min.js" type="text/javascript"></script>

</head>
<body>
    <form id="form1" runat="server">
    <div>
   
        <div id="summernote">Hello Summernote</div>
   
        <script>
            $(document).ready(function() {
                $('#summernote').summernote({
                    height: 300,
                    callbacks: {
                        onImageUpload: function(files) {
                            sendFile(files[0]);
                        }
                    }
                });

                function sendFile(file) {
                    data = new FormData();
                    data.append("file", file);
                    $.ajax({
                        url: 'GetFile.aspx',
                        data: data,
                        cache: false,
                        type: "POST",
                        contentType: false,
                        processData: false,
                        success: function(url) {
                            $('#summernote').summernote('insertImage', url);
                        }
                    });
                }
            });

            function funcMyHtml() {
                document.getElementById("HiddenField").value = $('#summernote').summernote('code');
            }
       
        </script>

        <asp:HiddenField ID="HiddenField" runat="server" />
        <asp:Button ID="Button1" runat="server" Text="Button" OnClientClick="funcMyHtml()" onclick="Button1_Click" />
    </div>
    </form>
</body>
</html>

이렇게...

참고로 여기서는 "callBacks : " 이라는 속성으로 콜백 함수를 지정하였는데, 인터넷을 뒤져 보면 대부분 그냥 "callBacks : " 없이 그냥 "onImageUpload() " 를 쓰라고 되어 있을 것이다. 근데 언제 부터인지 그렇게 안된다.

심지어는 "insertImage" 지정하는 방식도 다르다.
언제 부터 바뀌었는지는 모르겠지만, 하여튼 현재 (2017-05-31) 공식배포처에서 다운 받은 파일로는 안된다.

이렇게 해 놓았으면 파일을 서버로 전송할 준비는 다 되었다.

이제 서버측에서 파일을 수신하는 기능을 만들어 준다.


파일 수신받을 aspx 파일을 하나 추가해 주고...
(원래 웹서비스를 만드는게 정석이겠으나... 귀찮으니 그냥 만들자.)


단순히 파일을 서버에 저장하는 페이지 이므로 아무런 내용이 필요 없으니 html 은 모두 삭제 하자. 헤더만 남겨 두면 된다.



using System;
using System.IO;

public partial class SummerNote_GetFile : System.Web.UI.Page
{
    protected void Page_Load(object sender, EventArgs e)
    {
        foreach (string upload in Request.Files)
        {
            string path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory,"upload");
            string filename = Path.GetFileName(Request.Files[upload].FileName);

            if (!Directory.Exists(path)) Directory.CreateDirectory(path);

            Request.Files[upload].SaveAs(Path.Combine(path, filename));

            Response.Write("http://" + Request.Url.Host + ":" + Request.Url.Port + "/web/upload/" + filename);
        }
    }
}

그러고 나서 비하인드 코드에 파일을 저장하는 루틴을 기록.
이렇게 하면 POST 방식으로 전달한 파일을 서버에 저장 할수 있다.


쉽게 말해서 aspx 파일이 아닌 순수 Html 페이지에서 전달한 파일도 받아서 서버에 저장할수 있는 방식이다.
(여기서 FileUpload.htm 파일은 그냥 파일이 잘 업로드 되고 업로드된 경로가 잘 출력되나 테스트해보는 페이지 일 뿐. 즉, 없어도 상관 없는 파일이다.)

이렇게 해 놓고 썸머노트에서 이미지를 다시 삽입해 보면...


이렇게 이미지 태그 방식으로 이미지가 삽입된 것을 볼수 있다.
(참고로 완전한 Url 이 아니면 아예 이미지가 삽입되지 않는다.)

C#, WebForm : 위지윅 에디터 (Wysiwyg Editor) 썸머노트(summernote)

웹페이지를 구축할때 웹 에디터는 이제 빠질수 없는 구성요소다.
그런데, 예전에는 제법 많은 무료 에디터들이 있었지만, 이젠 거의 개발이 중단되었거나, 유료화 되어 버려 아쉽다. (네이버와 다음에서 공개한 에디터들을 많이 썼지만, 지금은 거의 업데이트가 안되고 있다.)

뭐, 돈많은 개발자들이야 좋은 유료 에디터를 갖다 쓰겠지만... ㅠㅠ... 돈이 없으니, 아쉬운대로 무료 에디터를 찾아 써야 한다.

그 중에서 무려 "국내" 개발진에서 개발하고 있는 무료 에디터가 있으니, "Summernote" 이다.

배포처 : http://summernote.org/


디자인도 성능도 여타 다른 에디터에 뒤지지 않는다.
쓰는 방법도 간단한데, 간단이 위의 다운로드 이미지를 클릭해서 다운 받거나...


"Getting Started" 페이지로 이동해 거기서 "Downloaded compiled" 버튼을 클릭해 다운 받아도 된다. 두 곳의 다운로드 파일은 약간의 차이가 있는데...


초기 페이지에서 다운 받을수 있는 파일 "summernote-master.zip" 는 기능 파일과 예제가 포함되어 있는 파일이고, 다운로드 페이지에서 받을수 있는 파일인 "summernote-0.8.4-dist.zip" 는 기능파일만이 포함되어 있는 파일이다.

물론 "summernote-master.zip" 에서 기능 폴더인 "dist" 폴더만 갖다 써도 된다. 어차피 같은 파일이니까. 하지만, "summernote-0.8.4-dist.zip" 에 있는 파일이 약간이나마 용량이 적으니, 실 서버에 사용할 때는 그쪽을 이용하는 것이 좋을 것이다.

에디터를 사용하려면, 사용하려는 페이지에 다운 받은 파일들을 복사해 넣은 후, jQuery 와 bootstrap 을 링크 시켜 줘야 한다.

그런 다음에는 에디터를 적용할 <div> 의 id 를 지정하여 주기만 하면 끝.


이렇게 하고 페이지를 로드해 보면...


이렇게 에디터가 적용이 된다.
적용이 되기는 하는데, 기본 크기가 너무 작으니 좀 키워 보자.


이렇게 초기 값을 지정해 놓고 다시 페이지를 로드해 보면...


이렇게 높이가 커진 에디터를 볼수 있다.

이 에디터에서 작성한 내용을 C# 에서 받아 볼려면, 여러가지 방법이 있겠지만, 제일 간단한 방법으로 <hidden> 필드를 이용하는 것을 추천한다.


이렇게 HiddenField 를 하나 두고, 버튼을 클릭시 자바스크립트를 이용해서 summernote 의 값을 세팅해두면...


이렇게 비하인드 코드에서 썸머노트의 값을 가져 올수 있다.
반대로 비하인드 코에서 썸머노트에 내용을 로드하게끔 할수도 있는데...


이렇게 초기화 함수에 히든필드 값을 읽어 오도록 지정해 놓고...


이렇게 비하인드 코드에서 히드필드의 값을 지정하면 ...


이렇게 특정 값이 세팅되게 할수도 있음.
이제 이렇게 하고, DB 연결만 해주면 게시판은 쉽게 만들수 있음.

IE : 개체가 'addEventListener' 속성이나 메서드를 지원하지 않습니다.

jQuery 를 테스트 해보다 보니, "개체가 'addEventListener' 속성이나 메서드를 지원하지 않습니다." ... 라는 오류가 발생한다. 문제는 크롬에선 정상적으로 로드 되는데, IE 에서만 오류가 발생한다는 것.


아무것도 안하고 jQuery 링크만 시켰을 뿐인데, 오류가 발생한다.
황당하지만, 구글신께 여쭤보니...

일반적으로 문서 시작에 "<!DOCTYPE html>" 같은 문서 타입을 기록해주지 않았을 경우이거나, "호환성 보기 설정" 이 되어 있을 경우 발생한다고 한다.

문서 타입은 그냥 그 파일을 열어서 기록되어 있나 확인해보면 되고...
호환성 보기는...


IE 에서 "도구 > 호환성 보기 설정" 으로 확인.


역시 호환성 보기가 설정 되어 있었군.
이거 제거를 해주면 정상적으로 페이지가 오류 없이 로드 된다.


2017년 5월 26일 금요일

C#, MVC, WebApp : EntityFramework 로 DB 연결 및 조회


참고 : C#, MVC, WebApp : MVC 웹 응용프로그램 생성 및 시작페이지 변경


 무슨 장점이 있는지는 모르겠으나, 요즘은 EntityFramework 로 DB 를 관리하는 경우가 많으니, 그냥 EntityFramework 로 해본다.

일단 "NuGet 패키지 관리" 를 실행.


여기서 "EntityFramework" 를 검색해서 설치한다.


그럼 참조에 "EntityFramework" 관련 항목이 추가된다.
일단 설치는 끝.


 이걸 사용하려면, 일단 폴더를 하나 생성해 주고.


여기서는 "DataContext" 라고 했지만, 아무거나 원하는대로 써도 됨.


여기다 클래슬 하나 추가해 준다.


이름은 적당히 알기 쉬운걸로...


그 클래스에 다른걸 별로 할것 없고, 그 클래스에 "DbContext" 를 상속 받게끔만 해주면 된다.

using System.Data.Entity;

namespace son10001.DataContext
{
    public class DBConnect : DbContext
    {
        public DBConnect() : base()
        {
        }
    }
}

이러면 DB 를 자동으로 연결해 주는데, 그래도 DB 연결 정보는 적어 줘야 접속이 된다.


DB 연결 정보는 "Web.config" 에 "<connectionStrings>" 항목으로 기록해 주면됨.
주의 할점은 <connectionStrings> 의 "name" 을 "DbContext" 클래스 명과 동일하게 지정해 주어야 한다는 것.

  <connectionStrings>
    <add name="DBconnect" connectionString="Data Source=192.168.0.100;Initial Catalog=Solution01;Persist Security Info=True;User ID=sa;Password=0000;" providerName="System.Data.SqlClient" />
  </connectionStrings>



DB 테이블은 이미 만들어 놓은 것을 쓴다.
여기서는 "Users" 테이블을 사용.


일단 테이블을 정의해 주어야 하니, "Models" 폴더에 클래스를 하나 추가해 준다.


이 클래스 명은 사용할 테이블명과 동일하게 주어야 함.



using System;
using System.ComponentModel.DataAnnotations;

namespace son10001.Models
{
    public class Users
    {
        [Key]
        public int UserNo { get; set; }

        [Required]
        public String UserId { get; set; }

        [Required]
        public String UserName { get; set; }

        [Required]
        public String UserPassword { get; set; }
    }
}

그리고 테이블 데이터셋과 동일하게 변수를 선언해 준다.
여기서 [Key] 는 기본키, [Required] 는 필수값. 즉, Not Null 을 의미한다.


using son10001.Models;
using System.Data.Entity;

namespace son10001.DataContext
{
    public class DBConnect : DbContext
    {
        public DBConnect() : base()
        {
        }

        public DbSet<Users> Users { get; set; }

    }
}

이제 테이블 설정을 만들어 놨으니, 이걸 DbContext 에 등록.
이렇게 함으로서 해당 테이블을 사용할 준비는 모두 끝났다.

이제 사용하기만 하면 됨.

이걸 사용하는 샘플.


var list = new List<Users>();
using (var db = new DBConnect())
{
    list = db.Users.ToList();
}

이전에 "Contact" 페이지를 기본 페이지로 지정했으니, 저기에다 데이터를 가져 오는 로직을 한번 추가해 보자.


이렇게 하고 실행하고, 저 지점에 중단점을 지정해서 놓고 가져온 값을 확인해 보면, DB 에서 현재 데이터를 잘 가져 왔음을 볼 수 있다.

참고로, DB Connect String 을 임의의 값으로 지정하는 법.


이렇게 <connectionStrings> 의 "Name" 을 임의의 값으로 지정했거나, <connectionStrings> 값 자체가 여러개 일경우. 즉, 테이트 서버와 운영서버의 연결값을 별도로 지정하여 사용한다던가 할때.... 저 값을 어떻게 지정해 줄수 있느냐? 하면...


그냥 DbContext 에서 base() 에 "name=연결문자열" 로 지정하면 간단히 지정 할 수 있다.
이렇게 하면 굳이 클래스명과 일치하지 않아도 쓸수 있음.



2017년 5월 24일 수요일

C#, MVC, WebApp : MVC 웹 응용프로그램 생성 및 시작페이지 변경


일단 프로젝트를 생성한다.


웹 페이지를 만들 것이므로, 웹 응용 프로그램을 선택.
이름은 적당히 주면 되고...


MVC 로 만들 것이므로 MVC 를 선택.
"비어있음" 을 선택하고 "MVC" 체크 박스를 체크해서 MVC 사이트를 만들수도 있다.
시작을 "MVC" 로 선택하고 하면 기본적인 틀을 미리 잡아 주는 것일뿐...


이렇게 사이트를 생성하고, 사이트를 "실행" 해보면...


요렇게 뭔가 작업하는 화면이 나오고 (VisualStudio 2017 이전 버젼이면 안나옴) ...


이렇게 어느정도 틀이 잡힌 웹페이지가 만들어져 나온다.
만약 "비어있음" 을 선택해서 사이트를 만들었으면, 그냥 빈 페이지가 나올 것이다.

문제는 지금 보이는 페이지가 도대체 어디에 있는 페이지냐는 것.


파일 위치 자체는 "View > Home > index.cshtml" 에 있다.
만약 이걸 다른 파일로 바꾸고 싶으면 어디를 변경해야 할까?


시작 페이지 설정은 "App_Start > RouteConfig.cs" 에 있다.
그 파일에 보면 "routes.MapRoute" 이라는 항목에 "Default" 로 등록되어 있으며, controller = "Home", action = "Index" 로 기록되어 있을 것이다.

즉, 현재 기본 페이지는, "Home" 컨트롤에 "Index" 라는 액션이 기본 페이지로 지정되어 있으며, "Home" 컨트롤은 "Controllers" 폴더에 있으며, 컨트롤명이 "Home" 이 되어 있으니, 클래스 파일은 "HomeController.cs" 가 된다.


"HomeController.cs" 파일을 보면 어러개의 액션이 지정되어 있으며, 시작 페이지 설정에 "Index" 가 지정도어 있으므로, "HomeController.cs" 내의 "Index()" 메소드가 실행되며, 이 메소드에서 View() 를 호출하면 자동으로 "Views > Home > index.cshtml" 파일을 불러 오게 된다.

따라서 시작 페이지를 다른 페이지로 바꾸려면....


예를 들어서 "Index" 를 "Contact" 로 바꾸면, "Views > Home > Contact.cshtml" 를 불러 오게 된다.


그렇게 바꾸고 실행하게 되면, "Contact.cshtml" 페이지가 기본 페이지로 실행이 된다.