첨부 실행 코드는 나눔고딕코딩 폰트를 사용합니다.
728x90
반응형
728x170

TestProject.zip
다운로드

▶ Properties/launchSettings.json

{
    "iisSettings" :
    {
        "windowsAuthentication"   : false,
        "anonymousAuthentication" : true,
        "iisExpress"              :
        {
            "applicationUrl" : "http://localhost:1795",
            "sslPort"        : 44306
        }
    },
    "profiles" :
    {
        "IIS Express" :
        {
            "commandName"          : "IISExpress",
            "launchBrowser"        : true,
            "launchUrl"            : "NoticeBoard",
            "environmentVariables" :
            {
                "ASPNETCORE_ENVIRONMENT" : "Development"
            }
        },
        "TestProject" :
        {
            "commandName"          : "Project",
            "launchBrowser"        : true,
            "launchUrl"            : "NoticeBoard",
            "applicationUrl"       : "https://localhost:5001;http://localhost:5000",
            "environmentVariables" :
            {
                "ASPNETCORE_ENVIRONMENT" : "Development"
            }
        }
    }
}

 

728x90

 

▶ appsettings.json

{
    "ConnectionStrings" :
    {
        "DefaultConnection" : "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=TestDB;Integrated Security=True;"
    },
    "Logging" :
    {
        "LogLevel" :
        {
            "Default"                    : "Information",
            "Microsoft"                  : "Warning",
            "Microsoft.Hosting.Lifetime" : "Information"
        }
    },
    "AllowedHosts" : "*",
    "MainSettings" :
    {

        "SiteAdministrator" : "admin"

    }
}

 

300x250

 

▶ Database/TestDB.sql

CREATE TABLE dbo.Notice
(
    ID            INT           IDENTITY(1, 1) NOT NULL PRIMARY KEY -- ID
   ,[Name]        NVARCHAR(25)  NOT NULL                            -- 작성자명
   ,MailAddress   NVARCHAR(100) NULL                                -- 메일 주소
   ,Title         NVARCHAR(150) NOT NULL                            -- 제목
   ,WriteDate     DATETIME      DEFAULT GETDATE() NOT NULL          -- 작성일
   ,WriteIP       NVARCHAR(15)  NULL                                -- 작성 IP
   ,Content       NTEXT         NOT NULL                            -- 내용
   ,[Password]    NVARCHAR(20)  NULL                                -- 패스워드
   ,ReadCount     INT           DEFAULT 0                           -- 조회 수
   ,[Encoding]    NVARCHAR(10)  NOT NULL                            -- 인코딩(HTML/TEXT)
   ,Homepage      NVARCHAR(100) NULL                                -- 홈페이지
   ,UpdateDate    DATETIME      NULL                                -- 수정일
   ,UpdateIP      NVARCHAR(15)  NULL                                -- 수정 IP
   ,[FileName]    NVARCHAR(255) NULL                                -- 파일명
   ,FileSize      INT           DEFAULT 0                           -- 파일 크기
   ,DownloadCount INT           DEFAULT 0                           -- 다운로드 수
   ,ReferenceID   INT           NOT NULL                            -- 참조 ID
   ,ReplyLevel    INT           DEFAULT 0                           -- 답변 레벨
   ,ReplyOrder    INT           DEFAULT 0                           -- 답변 순서
   ,ReplyCount    INT           DEFAULT 0                           -- 답변 수
   ,ParentID      INT           DEFAULT 0                           -- 부모 ID
   ,CommentCount  INT           DEFAULT 0                           -- 댓글 수
   ,Category      NVARCHAR(50)  Default('FREE') Null                -- 카테고리 
)
GO

CREATE TABLE dbo.NoticeComment
(
    ID         INT            IDENTITY(1, 1) NOT NULL PRIMARY KEY -- ID
   ,NoticeID   INT            NOT NULL                            -- 게시판 ID
   ,[Name]     NVARCHAR(25)   NOT NULL                            -- 작성자명
   ,Comment    NVARCHAR(4000) NOT NULL                            -- 내용
   ,WriteDate  SMALLDATETIME  DEFAULT(GETDATE())                  -- 작성일
   ,[Password] NVARCHAR(20)   NOT NULL                            -- 패스워드
)
GO

CREATE PROCEDURE dbo.WriteNotice
    @Name        NVARCHAR(25)
   ,@MailAddress NVARCHAR(100)
   ,@Title       NVARCHAR(150)
   ,@WriteIP     NVARCHAR(15)
   ,@Content     NTEXT
   ,@Password    NVARCHAR(20)
   ,@Encoding    NVARCHAR(10)
   ,@Homepage    NVARCHAR(100)
   ,@FileName    NVARCHAR(255)
   ,@FileSize    INT
AS
    DECLARE @MaximumReferenceID INT;

    SELECT @MaximumReferenceID = MAX(ReferenceID) FROM dbo.Notice;

    IF @MaximumReferenceID IS NULL
    BEGIN
        SET @MaximumReferenceID = 1;
    END
    ELSE
    BEGIN
        SET @MaximumReferenceID = @MaximumReferenceID + 1;
    END

    INSERT INTO dbo.Notice
    (
        [Name]
       ,MailAddress
       ,Title
       ,WriteIP
       ,Content
       ,[Password]
       ,[Encoding]
       ,Homepage
       ,[ReferenceID]
       ,[FileName]
       ,[FileSize]
    )
    Values
    (
        @Name
       ,@MailAddress
       ,@Title
       ,@WriteIP
       ,@Content
       ,@Password
       ,@Encoding
       ,@Homepage
       ,@MaximumReferenceID
       ,@FileName
       ,@FileSize
    );
GO

CREATE PROCEDURE dbo.ListNotice
    @Page INT
AS
    WITH OrderedNoticeBoard
    AS
    (
        SELECT
            ID
           ,[Name]
           ,MailAddress
           ,Title
           ,WriteDate
           ,ReadCount
           ,ReferenceID
           ,ReplyLevel
           ,ReplyOrder
           ,ReplyCount
           ,ParentID
           ,CommentCount
           ,[FileName]
           ,FileSize
           ,DownloadCount
           ,ROW_NUMBER() OVER (ORDER BY ReferenceID DESC, ReplyOrder ASC) AS 'RowNumber'
        FROM dbo.Notice
    )
    SELECT *
    FROM   OrderedNoticeBoard
    WHERE  RowNumber BETWEEN @Page * 10 + 1 AND (@Page + 1) * 10;
GO

CREATE PROCEDURE dbo.ViewNotice
    @ID Int
As
    UPDATE dbo.Notice
    SET    ReadCount = ReadCount + 1
    WHERE  ID = @ID;

    SELECT *
    FROM   dbo.Notice
    WHERE  ID = @ID;
GO

CREATE PROCEDURE dbo.ReplyNotice
    @Name        NVARCHAR(25)
   ,@MailAddress NVARCHAR(100)
   ,@Title       NVARCHAR(150)
   ,@WriteIP     NVARCHAR(15)
   ,@Content     NTEXT
   ,@Password    NVARCHAR(20)
   ,@Encoding    NVARCHAR(10)
   ,@Homepage    NVARCHAR(100)
   ,@ParentID    INT
   ,@FileName    NVARCHAR(255)
   ,@FileSize    INT
AS
    DECLARE @MaximumReplyOrder INT;
    DECLARE @MaximumReplyCount INT;
    DECLARE @ParentReferenceID INT;
    DECLARE @ParentReplyLevel  INT;
    DECLARE @ParentReplyOrder  INT;

    UPDATE dbo.Notice
    Set    ReplyCount = ReplyCount + 1
    WHERE  ID = @ParentID;

    SELECT
        @MaximumReplyOrder = ReplyOrder
       ,@MaximumReplyCount = ReplyCount
    FROM  dbo.Notice
    WHERE ParentID   = @ParentID
    AND   ReplyOrder = (SELECT MAX(ReplyOrder) FROM dbo.Notice WHERE ParentID = @ParentID);

    IF @MaximumReplyOrder IS NULL
    BEGIN
        SELECT @MaximumReplyOrder = ReplyOrder
        FROM   dbo.Notice
        WHERE  ID = @ParentID;

        SET @MaximumReplyCount = 0;
    END

    SELECT
        @ParentReferenceID = ReferenceID
       ,@ParentReplyLevel  = ReplyLevel
    FROM  dbo.Notice
    WHERE ID = @ParentID;

    UPDATE dbo.Notice
    SET    ReplyOrder  = ReplyOrder + 1
    WHERE  ReferenceID = @ParentReferenceID And ReplyOrder > (@MaximumReplyOrder + @MaximumReplyCount);

    INSERT INTO dbo.Notice
    (
        [Name]
       ,MailAddress
       ,Title
       ,WriteIP
       ,Content
       ,[Password]
       ,[Encoding]
       ,Homepage
       ,ReferenceID
       ,ReplyLevel
       ,ReplyOrder
       ,ParentID
       ,[FileName]
       ,FileSize
    )
    VALUES
    (
        @Name
       ,@MailAddress
       ,@Title
       ,@WriteIP
       ,@Content
       ,@Password
       ,@Encoding
       ,@Homepage
       ,@ParentReferenceID
       ,@ParentReplyLevel + 1
       ,@MaximumReplyOrder + @MaximumReplyCount + 1
       ,@ParentID
       ,@FileName
       ,@FileSize
    );
GO

CREATE PROCEDURE dbo.GetNoticeCount
As
    Select Count(*) From dbo.Notice;
GO

CREATE PROCEDURE dbo.SearchNoticeCount
    @SearchField NVARCHAR(25)
   ,@SearchQuery NVARCHAR(25)
AS
    SET @SearchQuery = '%' + @SearchQuery + '%';

    SELECT COUNT(*)
    FROM   dbo.Notice
    WHERE
    (
        CASE @SearchField
            WHEN 'Name'    THEN [Name]
            WHEN 'Title'   THEN Title
            WHEN 'Content' THEN Content
            ELSE                @SearchQuery
        END
    )
    LIKE @SearchQuery;
GO

CREATE PROCEDURE dbo.DeleteNotice
    @ID       INT
   ,@Password NVARCHAR(30)
AS
    DECLARE @Count INT;

    SELECT @Count = COUNT(*)
    FROM   dbo.Notice
    WHERE  ID         = @ID
    AND    [Password] = @Password;

    IF @Count = 0
    BEGIN
        Return 0;
    END

    DECLARE @ReplyCount  INT;
    DECLARE @ReplyOrder  INT;
    DECLARE @ReferenceID INT;
    DECLARE @ParentID    INT;

    SELECT
        @ReplyCount  = ReplyCount
       ,@ReplyOrder  = ReplyOrder
       ,@ReferenceID = ReferenceID
       ,@ParentID    = ParentID
    FROM  dbo.Notice
    WHERE ID = @ID;

    IF @ReplyCount = 0
    BEGIN
        IF @ReplyOrder > 0
        BEGIN
            UPDATE dbo.Notice
            SET    ReplyOrder  = ReplyOrder - 1
            WHERE  ReferenceID = @ReferenceID
            AND    ReplyOrder  > @ReplyOrder;

            UPDATE Notice
            SET    ReplyCount = ReplyCount - 1
            WHERE  ID = @ParentID;
        END

        DELETE FROM dbo.Notice
        WHERE ID = @ID;

        DELETE FROM dbo.Notice
        WHERE  ID         = @ParentID
        AND    UpdateIP   = N'((DELETED))'
        AND    ReplyCount = 0;
    END
    ELSE
    BEGIN
        UPDATE dbo.Notice
        SET
            [Name]       = N'(Unknown)',
            MailAddress  = '',
            [Password]   = '',
            Title        = N'(삭제된 글입니다.)',
            Content      = N'(삭제된 글입니다. 현재 답변이 포함되어 있기 때문에 내용만 삭제되었습니다.)',
            UpdateIP     = N'((DELETED))',
            [FileName]   = '',
            FileSize     = 0,
            CommentCount = 0
        WHERE ID = @ID;
    END
GO

CREATE PROCEDURE dbo.UpdateNotice
    @Name        NVARCHAR(25) 
   ,@MailAddress NVARCHAR(100)
   ,@Title       NVARCHAR(150)
   ,@UpdateIP    NVARCHAR(15) 
   ,@Content     NTEXT        
   ,@Password    NVARCHAR(30) 
   ,@Encoding    NVARCHAR(10) 
   ,@Homepage    NVARCHAR(100)
   ,@FileName    NVARCHAR(255)
   ,@FileSize    INT          
   ,@ID          INT
AS
    DECLARE @Count INT

    SELECT @Count = Count(*)
    FROM   dbo.Notice
    WHERE  ID         = @ID
    AND    [Password] = @Password;

    IF @Count > 0
    BEGIN
        UPDATE dbo.Notice
        SET
            [Name]      = @Name
           ,MailAddress = @MailAddress
           ,Title       = @Title
           ,UpdateIP    = @UpdateIP
           ,UpdateDate  = GETDATE()
           ,Content     = @Content
           ,[Encoding]  = @Encoding
           ,Homepage    = @Homepage
           ,[FileName]  = @FileName
           ,FileSize    = @FileSize
        Where ID = @ID;

        SELECT '1';
    END
    ELSE
        SELECT '0';
GO

CREATE PROCEDURE dbo.SearchNotice
    @Page        INT
   ,@SearchField NVARCHAR(25)
   ,@SearchQuery NVARCHAR(25)
AS
    WITH OrderedNoticeBoard
    AS
    (
        SELECT
            ID
           ,[Name]
           ,MailAddress
           ,Title
           ,WriteDate
           ,ReadCount
           ,ReferenceID
           ,ReplyLevel
           ,ReplyOrder
           ,ReplyCount
           ,ParentID
           ,CommentCount
           ,[FileName]
           ,FileSize
           ,DownloadCount
           ,ROW_NUMBER() OVER (ORDER BY ReferenceID DESC, ReplyOrder ASC) AS 'RowNumber'
        FROM dbo.Notice
        WHERE
        (
            CASE @SearchField
                WHEN 'Name'    THEN [Name]
                WHEN 'Title'   THEN Title
                WHEN 'Content' THEN Content
                ELSE                @SearchQuery
            END
        )
        LIKE '%' + @SearchQuery + '%'
    )
    SELECT
        ID
       ,[Name]
       ,MailAddress
       ,Title
       ,WriteDate
       ,ReadCount
       ,ReferenceID
       ,ReplyLevel
       ,ReplyOrder
       ,ReplyCount
       ,ParentID
       ,CommentCount
       ,[FileName]
       ,FileSize
       ,DownloadCount
       ,RowNumber
    FROM  OrderedNoticeBoard
    WHERE RowNumber BETWEEN @Page * 10 + 1 AND (@Page + 1) * 10
    ORDER BY ID DESC;
GO

 

▶ Setings/MainSettings.cs

namespace TestProject.Settings
{
    /// <summary>
    /// 메인 설정
    /// </summary>
    public class MainSettings
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 사이트 관리자 - SiteAdministrator

        /// <summary>
        /// 사이트 관리자
        /// </summary>
        public string SiteAdministrator { get; set; }

        #endregion
    }
}

 

▶ FileHelper.cs

using System.IO;

namespace TestProject
{
    /// <summary>
    /// 파일 헬퍼
    /// </summary>
    public class FileHelper
    {
        ////////////////////////////////////////////////////////////////////////////////////////// Method
        //////////////////////////////////////////////////////////////////////////////// Static
        ////////////////////////////////////////////////////////////////////// Public

        #region 유니크 파일명 구하기 - GetUniqueFileName(sourceDirectoryPath, fileName)

        /// <summary>
        /// 유니크 파일명 구하기
        /// </summary>
        /// <param name="sourceDirectoryPath">소스 디렉토리 경로</param>
        /// <param name="fileName">파일명</param>
        /// <returns>유니크 파일명</returns>
        public static string GetUniqueFileName(string sourceDirectoryPath, string fileName)
        {
            string temporaryName = Path.GetFileNameWithoutExtension(fileName);
            string fileExtension = Path.GetExtension(fileName);

            bool exist = true;

            int i = 0;

            while(exist)
            {
                if(File.Exists(Path.Combine(sourceDirectoryPath, fileName)))
                {
                    fileName = temporaryName + "(" + ++i + ")" + fileExtension;
                }
                else
                {
                    exist = false;
                }
            }

            return fileName;
        }

        #endregion
    }
}

 

▶ HTMLHelper.cs

namespace TestProject
{
    /// <summary>
    /// HTML 헬퍼
    /// </summary>
    public class HTMLHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 인코딩하기 - Encode(source)

        /// <summary>
        /// 인코딩하기
        /// </summary>
        /// <param name="source">소스 문자열</param>
        /// <returns>인코딩 문자열</returns>
        public static string Encode(string source)
        {
            string target = string.Empty;

            if(string.IsNullOrEmpty(source))
            {
                target = string.Empty;
            }
            else
            {
                target = source;
                target = target.Replace("&"   , "&amp;" );
                target = target.Replace(">"   , "&gt;"  );
                target = target.Replace("<"   , "&lt;"  );
                target = target.Replace("\r\n", "<br />");
                target = target.Replace("\""  , "&#34;" );
            }

            return target;
        }

        #endregion
        #region 탭/공백 포함 인코딩하기 - EncodeIncludingTabAndSpace(source)

        /// <summary>
        /// 탭/공백 포함 인코딩하기
        /// </summary>
        /// <param name="source">소스 문자열</param>
        /// <returns>인코딩 문자열</returns>
        public static string EncodeIncludingTabAndSpace(string source)
        {
            return Encode(source)
                .Replace("\t"     , "&nbsp;&nbsp;&nbsp;&nbsp;")
                .Replace(" " + " ", "&nbsp;&nbsp;");
        }

        #endregion
    }
}

 

▶ NoticeHelper.cs

using System;
using System.IO;

namespace TestProject
{
    /// <summary>
    /// 게시판 헬퍼
    /// </summary>
    public class NoticeHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region REPLY 이미지 HTML 구하기 - GetReplyImageHTML(stepObject)

        /// <summary>
        /// REPLY 이미지 HTML 구하기
        /// </summary>
        /// <param name="stepObject">단계 객체</param>
        /// <returns>REPLY 이미지 HTML</returns>
        public static string GetReplyImageHTML(object stepObject)
        {
            int step = Convert.ToInt32(stepObject);

            string target = string.Empty;

            if(step == 0)
            {
                target = string.Empty;
            }
            else
            {
                for(int i = 0; i < step; i++)
                {
                    target = string.Format
                    (
                        "<img src=\"{0}\" height=\"{1}\" width=\"{2}\">",
                        "/Image/NoticeBoard/blank.gif",
                        "0",
                        (step * 15)
                    );
                }

                target += "<img src=\"/Image/NoticeBoard/reply.gif\">";
            }

            return target;
        }

        #endregion
        #region 댓글 수 HTML 구하기 - GetCommentCountHTML(commentCountObject)

        /// <summary>
        /// 댓글 수 HTML 구하기
        /// </summary>
        /// <param name="commentCountObject">댓글 수 객체</param>
        /// <returns>댓글 수 HTML</returns>
        public static string GetCommentCountHTML(object commentCountObject)
        {
            string target = string.Empty;

            try
            {
                if(Convert.ToInt32(commentCountObject) > 0)
                {
                    target =  "<img src=\"/Image/NoticeBoard/comment.gif\" />";
                    target += " (" + commentCountObject.ToString() + ")";
                }
            }
            catch(Exception)
            {
                target = string.Empty;
            }

            return target;
        }

        #endregion
        #region 신규 이미지 HTML 구하기 - GetNewImageHTML(dateObject)

        /// <summary>
        /// 신규 이미지 HTML 구하기
        /// </summary>
        /// <param name="dateObject">날짜 객체</param>
        /// <returns>신규 이미지 HTML</returns>
        public static string GetNewImageHTML(object dateObject)
        {
            if(dateObject != null)
            {
                if(!string.IsNullOrEmpty(dateObject.ToString()))
                {
                    DateTime date = Convert.ToDateTime(dateObject);

                    TimeSpan timeSpan = DateTime.Now - date;

                    string target = string.Empty;

                    if(timeSpan.TotalMinutes < 1440)
                    {
                        target = "<img src=\"/Image/NoticeBoard/new.gif\">";
                    }

                    return target;
                }
            }

            return string.Empty;
        }

        #endregion
        #region 날짜/시간 HTML 구하기 - GetDateTimeHTML(dateTimeObject)

        /// <summary>
        /// 날짜/시간 HTML 구하기
        /// </summary>
        /// <param name="dateTimeObject">날짜/시간 객체</param>
        /// <returns>날짜/시간 HTML</returns>
        public static string GetDateTimeHTML(object dateTimeObject)
        {
            if(dateTimeObject != null)
            {
                if(!string.IsNullOrEmpty(dateTimeObject.ToString()))
                {
                    string writeDateString = Convert.ToDateTime(dateTimeObject).ToString("yyyy-MM-dd");

                    if(writeDateString == DateTime.Now.ToString("yyyy-MM-dd"))
                    {
                        return Convert.ToDateTime(dateTimeObject).ToString("hh:mm:ss");
                    }
                    else
                    {
                        return writeDateString;
                    }
                }
            }

            return "-";
        }

        #endregion
        #region 파일 크기 문자열 구하기 - GetFileSizeString(fileSize)

        /// <summary>
        /// 파일 크기 문자열 구하기
        /// </summary>
        /// <param name="fileSize">파일 크기</param>
        /// <returns>파일 크기 문자열</returns>
        public static string GetFileSizeString(int fileSize)
        {
            string target = string.Empty;

            if(fileSize >= 1048576)
            {
                target = string.Format("{0:F} MB", (fileSize / 1048576));
            }
            else
            {
                if(fileSize >= 1024)
                {
                    target = string.Format("{0} KB", (fileSize / 1024));
                }
                else
                {
                    target = fileSize + " Byte(s)";
                }
            }

            return target;
        }

        #endregion
        #region 이미지 파일 여부 구하기 - IsImageFile(filePath)

        /// <summary>
        /// 이미지 파일 여부 구하기
        /// </summary>
        /// <param name="filePath">파일 경로</param>
        /// <returns>이미지 파일 여부</returns>
        public static bool IsImageFile(string filePath)
        {
            string fileExtension = Path.GetExtension(filePath).Replace(".", "").ToLower();

            bool result = false;

            if(fileExtension == "gif" || fileExtension == "jpg" || fileExtension == "jpeg" || fileExtension == "png")
            {
                result = true;
            }
            else
            {
                result = false;
            }

            return result;
        }

        #endregion
        #region 파일 다운로드 링크 HTML 구하기 - GetFileDownloadLinkHTML(id, filePath, fileSizeString)

        /// <summary>
        /// 파일 다운로드 링크 HTML 구하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <param name="filePath">파일 경로</param>
        /// <param name="fileSizeString">파일 크기 문자열</param>
        /// <returns>파일 다운로드 링크 HTML 구하기</returns>
        public static string GetFileDownloadLinkHTML(int id, string filePath, string fileSizeString)
        {
            if(filePath.Length > 0)
            {
                return string.Format
                (
                    "<a href=\"/NoticeBoard/Download?id={0}\">{1}</a>",
                    id.ToString(),
                    GetFileExtensionImageHTML(filePath, filePath + "(" + GetFileSizeString(Convert.ToInt32(fileSizeString)) + ")")
                );
            }
            else
            {
                return "-";
            }
        }

        #endregion
        #region 파일 확장자 이미지 HTML 구하기 - GetFileExtensionImageHTML(filePath, alternateString)

        /// <summary>
        /// 파일 확장자 이미지 HTML 구하기
        /// </summary>
        /// <param name="filePath">파일 이름</param>
        /// <param name="alternateString">대체 문자열</param>
        /// <returns>파일 확장자 이미지 HTML</returns>
        public static string GetFileExtensionImageHTML(string filePath, string alternateString)
        {
            string format = "<img src='{0}' border='0' alt='{1}'>";

            string fileExtension = Path.GetExtension(filePath).Replace(".", "").ToLower();

            string target = string.Empty;

            switch(fileExtension)
            {
                case "ace"     : target = string.Format(format, "/Image/FileExtension/ext_ace.gif"    , alternateString); break;
                case "ai"      : target = string.Format(format, "/Image/FileExtension/ext_ai.gif"     , alternateString); break;
                case "alz"     : target = string.Format(format, "/Image/FileExtension/ext_alz.gif"    , alternateString); break;
                case "arj"     : target = string.Format(format, "/Image/FileExtension/ext_arj.gif"    , alternateString); break;
                case "asa"     : target = string.Format(format, "/Image/FileExtension/ext_asa.gif"    , alternateString); break;
                case "asax"    : target = string.Format(format, "/Image/FileExtension/ext_asax.gif"   , alternateString); break;
                case "ascx"    : target = string.Format(format, "/Image/FileExtension/ext_ascx.gif"   , alternateString); break;
                case "asf"     : target = string.Format(format, "/Image/FileExtension/ext_asf.gif"    , alternateString); break;
                case "asmx"    : target = string.Format(format, "/Image/FileExtension/ext_asmx.gif"   , alternateString); break;
                case "asp"     : target = string.Format(format, "/Image/FileExtension/ext_asp.gif"    , alternateString); break;
                case "aspx"    : target = string.Format(format, "/Image/FileExtension/ext_aspx.gif"   , alternateString); break;
                case "asx"     : target = string.Format(format, "/Image/FileExtension/ext_asx.gif"    , alternateString); break;
                case "au"      : target = string.Format(format, "/Image/FileExtension/ext_au.gif"     , alternateString); break;
                case "avi"     : target = string.Format(format, "/Image/FileExtension/ext_avi.gif"    , alternateString); break;
                case "bat"     : target = string.Format(format, "/Image/FileExtension/ext_bat.gif"    , alternateString); break;
                case "bmp"     : target = string.Format(format, "/Image/FileExtension/ext_bmp.gif"    , alternateString); break;
                case "c"       : target = string.Format(format, "/Image/FileExtension/ext_c.gif"      , alternateString); break;
                case "cs"      : target = string.Format(format, "/Image/FileExtension/ext_cs.gif"     , alternateString); break;
                case "csproj"  : target = string.Format(format, "/Image/FileExtension/ext_csproj.gif" , alternateString); break;
                case "cab"     : target = string.Format(format, "/Image/FileExtension/ext_cab.gif"    , alternateString); break;
                case "chm"     : target = string.Format(format, "/Image/FileExtension/ext_chm.gif"    , alternateString); break;
                case "com"     : target = string.Format(format, "/Image/FileExtension/ext_com.gif"    , alternateString); break;
                case "config"  : target = string.Format(format, "/Image/FileExtension/ext_config.gif" , alternateString); break;
                case "cpp"     : target = string.Format(format, "/Image/FileExtension/ext_cpp.gif"    , alternateString); break;
                case "css"     : target = string.Format(format, "/Image/FileExtension/ext_css.gif"    , alternateString); break;
                case "csv"     : target = string.Format(format, "/Image/FileExtension/ext_csv.gif"    , alternateString); break;
                case "disco"   : target = string.Format(format, "/Image/FileExtension/ext_disco.gif"  , alternateString); break;
                case "dll"     : target = string.Format(format, "/Image/FileExtension/ext_dll.gif"    , alternateString); break;
                case "doc"     : target = string.Format(format, "/Image/FileExtension/ext_doc.gif"    , alternateString); break;
                case "eml"     : target = string.Format(format, "/Image/FileExtension/ext_eml.gif"    , alternateString); break;
                case "exe"     : target = string.Format(format, "/Image/FileExtension/ext_exe.gif"    , alternateString); break;
                case "gif"     : target = string.Format(format, "/Image/FileExtension/ext_gif.gif"    , alternateString); break;
                case "gz"      : target = string.Format(format, "/Image/FileExtension/ext_gz.gif"     , alternateString); break;
                case "h"       : target = string.Format(format, "/Image/FileExtension/ext_h.gif"      , alternateString); break;
                case "hlp"     : target = string.Format(format, "/Image/FileExtension/ext_hlp.gif"    , alternateString); break;
                case "htm"     : target = string.Format(format, "/Image/FileExtension/ext_htm.gif"    , alternateString); break;
                case "html"    : target = string.Format(format, "/Image/FileExtension/ext_html.gif"   , alternateString); break;
                case "hwp"     : target = string.Format(format, "/Image/FileExtension/ext_hwp.gif"    , alternateString); break;
                case "hwt"     : target = string.Format(format, "/Image/FileExtension/ext_hwt.gif"    , alternateString); break;
                case "inc"     : target = string.Format(format, "/Image/FileExtension/ext_inc.gif"    , alternateString); break;
                case "ini"     : target = string.Format(format, "/Image/FileExtension/ext_ini.gif"    , alternateString); break;
                case "jpg"     : target = string.Format(format, "/Image/FileExtension/ext_jpg.gif"    , alternateString); break;
                case "jpeg"    : target = string.Format(format, "/Image/FileExtension/ext_jpeg.gif"   , alternateString); break;
                case "js"      : target = string.Format(format, "/Image/FileExtension/ext_js.gif"     , alternateString); break;
                case "lzh"     : target = string.Format(format, "/Image/FileExtension/ext_lzh.gif"    , alternateString); break;
                case "m3u"     : target = string.Format(format, "/Image/FileExtension/ext_m3u.gif"    , alternateString); break;
                case "max"     : target = string.Format(format, "/Image/FileExtension/ext_max.gif"    , alternateString); break;
                case "mdb"     : target = string.Format(format, "/Image/FileExtension/ext_mdb.gif"    , alternateString); break;
                case "mid"     : target = string.Format(format, "/Image/FileExtension/ext_mid.gif"    , alternateString); break;
                case "mov"     : target = string.Format(format, "/Image/FileExtension/ext_mov.gif"    , alternateString); break;
                case "mp2"     : target = string.Format(format, "/Image/FileExtension/ext_mp2.gif"    , alternateString); break;
                case "mp3"     : target = string.Format(format, "/Image/FileExtension/ext_mp3.gif"    , alternateString); break;
                case "mpe"     : target = string.Format(format, "/Image/FileExtension/ext_mpe.gif"    , alternateString); break;
                case "mpeg"    : target = string.Format(format, "/Image/FileExtension/ext_mpeg.gif"   , alternateString); break;
                case "mpg"     : target = string.Format(format, "/Image/FileExtension/ext_mpg.gif"    , alternateString); break;
                case "msi"     : target = string.Format(format, "/Image/FileExtension/ext_exe.gif"    , alternateString); break;
                case ""        : target = string.Format(format, "/Image/FileExtension/ext_none.gif"   , alternateString); break;
                case "pcx"     : target = string.Format(format, "/Image/FileExtension/ext_pcx.gif"    , alternateString); break;
                case "pdb"     : target = string.Format(format, "/Image/FileExtension/ext_pdb.gif"    , alternateString); break;
                case "pdf"     : target = string.Format(format, "/Image/FileExtension/ext_pdf.gif"    , alternateString); break;
                case "pls"     : target = string.Format(format, "/Image/FileExtension/ext_pls.gif"    , alternateString); break;
                case "png"     : target = string.Format(format, "/Image/FileExtension/ext_png.gif"    , alternateString); break;
                case "ppt"     : target = string.Format(format, "/Image/FileExtension/ext_ppt.gif"    , alternateString); break;
                case "psd"     : target = string.Format(format, "/Image/FileExtension/ext_psd.gif"    , alternateString); break;
                case "ra"      : target = string.Format(format, "/Image/FileExtension/ext_ra.gif"     , alternateString); break;
                case "ram"     : target = string.Format(format, "/Image/FileExtension/ext_ram.gif"    , alternateString); break;
                case "rar"     : target = string.Format(format, "/Image/FileExtension/ext_rar.gif"    , alternateString); break;
                case "reg"     : target = string.Format(format, "/Image/FileExtension/ext_reg.gif"    , alternateString); break;
                case "resx"    : target = string.Format(format, "/Image/FileExtension/ext_resx.gif"   , alternateString); break;
                case "rm"      : target = string.Format(format, "/Image/FileExtension/ext_rm.gif"     , alternateString); break;
                case "rmi"     : target = string.Format(format, "/Image/FileExtension/ext_rmi.gif"    , alternateString); break;
                case "rtf"     : target = string.Format(format, "/Image/FileExtension/ext_rtf.gif"    , alternateString); break;
                case "sql"     : target = string.Format(format, "/Image/FileExtension/ext_sql.gif"    , alternateString); break;
                case "swf"     : target = string.Format(format, "/Image/FileExtension/ext_swf.gif"    , alternateString); break;
                case "sys"     : target = string.Format(format, "/Image/FileExtension/ext_sys.gif"    , alternateString); break;
                case "tar"     : target = string.Format(format, "/Image/FileExtension/ext_tar.gif"    , alternateString); break;
                case "tga"     : target = string.Format(format, "/Image/FileExtension/ext_tga.gif"    , alternateString); break;
                case "tif"     : target = string.Format(format, "/Image/FileExtension/ext_tif.gif"    , alternateString); break;
                case "ttf"     : target = string.Format(format, "/Image/FileExtension/ext_ttf.gif"    , alternateString); break;
                case "txt"     : target = string.Format(format, "/Image/FileExtension/ext_txt.gif"    , alternateString); break;
                case "vb"      : target = string.Format(format, "/Image/FileExtension/ext_vb.gif"     , alternateString); break;
                case "vbs"     : target = string.Format(format, "/Image/FileExtension/ext_vbs.gif"    , alternateString); break;
                case "vbdisco" : target = string.Format(format, "/Image/FileExtension/ext_vbdisco.gif", alternateString); break;
                case "wav"     : target = string.Format(format, "/Image/FileExtension/ext_wav.gif"    , alternateString); break;
                case "wax"     : target = string.Format(format, "/Image/FileExtension/ext_wax.gif"    , alternateString); break;
                case "webinfo" : target = string.Format(format, "/Image/FileExtension/ext_webinfo.gif", alternateString); break;
                case "wma"     : target = string.Format(format, "/Image/FileExtension/ext_wma.gif"    , alternateString); break;
                case "wmv"     : target = string.Format(format, "/Image/FileExtension/ext_wmv.gif"    , alternateString); break;
                case "wmx"     : target = string.Format(format, "/Image/FileExtension/ext_wmx.gif"    , alternateString); break;
                case "wri"     : target = string.Format(format, "/Image/FileExtension/ext_wri.gif"    , alternateString); break;
                case "wvx"     : target = string.Format(format, "/Image/FileExtension/ext_wvx.gif"    , alternateString); break;
                case "xls"     : target = string.Format(format, "/Image/FileExtension/ext_xls.gif"    , alternateString); break;
                case "xml"     : target = string.Format(format, "/Image/FileExtension/ext_xml.gif"    , alternateString); break;
                case "zip"     : target = string.Format(format, "/Image/FileExtension/ext_zip.gif"    , alternateString); break;
                default        : target = string.Format(format, "/Image/FileExtension/ext_unknown.gif", alternateString); break;
            }

            return target;
        }

        #endregion
    }
}

 

▶ StringHelper.cs

using System.Globalization;

namespace TestProject
{
    /// <summary>
    /// 문자열 헬퍼
    /// </summary>
    public static class StringHelper
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 문자열 잘라내기 - CutString(source, length)

        /// <summary>
        /// 문자열 잘라내기
        /// </summary>
        /// <param name="source">소스 문자열</param>
        /// <param name="length">길이</param>
        /// <returns>잘라낸 문자열</returns>
        public static string CutString(this string source, int length)
        {
            if(source.Length > (length - 3))
            {
                return source.Substring(0, length - 3) + "...";
            }

            return source;
        }

        #endregion
        #region 유니코드 문자열 잘라내기 - CutUnicodeString(source, length)

        /// <summary>
        /// 유니코드 문자열 잘라내기
        /// </summary>
        /// <param name="source">소스 문자열</param>
        /// <param name="length">길이</param>
        /// <returns>잘라낸 유니코드 문자열</returns>
        public static string CutUnicodeString(this string source, int length)
        {
            string target = source;

            StringInfo stringInfo = new StringInfo(source);

            if(stringInfo.LengthInTextElements > (length - 3))
            {
                target = stringInfo.SubstringByTextElements(0, length - 3) + "...";
            }

            return target;
        }

        #endregion
    }
}

 

▶ Models/BoardWriteFormType.cs

namespace TestProject.Models
{
    /// <summary>
    /// 게시판 쓰기 폼 타입
    /// </summary>
    public enum BoardWriteFormType
    {
        /// <summary>
        /// 글 쓰기 페이지
        /// </summary>
        Write,

        /// <summary>
        /// 글 수정 페이지
        /// </summary>
        Modify,

        /// <summary>
        /// 글 답변 페이지
        /// </summary>
        Reply
    }
}

 

▶ ContentEncodingType.cs

namespace TestProject.Models
{
    /// <summary>
    /// 내용 인코딩 타입
    /// </summary>
    public enum ContentEncodingType
    {
        /// <summary>
        /// 텍스트
        /// </summary>
        Text,

        /// <summary>
        /// HTML로 실행
        /// </summary>
        HTML,

        /// <summary>
        /// HTML + 개행(\r\n) 적용
        /// </summary>
        Mixed
    }
}

 

▶ Models/NoticeModel.cs

using System;
using System.ComponentModel.DataAnnotations;

namespace TestProject.Models
{
    /// <summary>
    /// 게시글 모델
    /// </summary>
    public class NoticeModel
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region ID - ID

        /// <summary>
        /// ID
        /// </summary>
        [Display(Name = "ID")]
        public int ID { get; set; }

        #endregion
        #region 작성자명 - Name

        /// <summary>
        /// 작성자명
        /// </summary>
        [Display(Name = "작성자명")]
        [Required(ErrorMessage = "작성자명을 입력해 주시기 바랍니다.")]
        public string Name { get; set; }

        #endregion
        #region 메일 주소 - MailAddress

        /// <summary>
        /// 메일 주소
        /// </summary>
        [EmailAddress(ErrorMessage = "메일 주소를 입력해 주시기 바랍니다.")]
        public string MailAddress { get; set; }

        #endregion
        #region 제목 - Title

        /// <summary>
        /// 제목
        /// </summary>
        [Display(Name = "제목")]
        [Required(ErrorMessage = "제목을 입력해 주시기 바랍니다.")]
        public string Title { get; set; }

        #endregion
        #region 작성일 - WriteDate

        /// <summary>
        /// 작성일
        /// </summary>
        [Display(Name = "작성일")]
        public DateTime WriteDate { get; set; }

        #endregion
        #region 작성 IP - WriteIP

        /// <summary>
        /// 작성 IP
        /// </summary>
        public string WriteIP { get; set; }

        #endregion
        #region 내용 - Content

        /// <summary>
        /// 내용
        /// </summary>
        [Display(Name = "내용")]
        [Required(ErrorMessage = "내용을 입력해 주시기 바랍니다.")]
        public string Content { get; set; }

        #endregion
        #region 패스워드 - Password

        /// <summary>
        /// 패스워드
        /// </summary>
        [Display(Name = "패스워드")]
        [Required(ErrorMessage = "패스워드를 입력해 주시기 바랍니다.")]
        public string Password { get; set; }

        #endregion
        #region 조회 수 - ReadCount

        /// <summary>
        /// 조회 수
        /// </summary>
        [Display(Name = "조회 수")]
        public int ReadCount { get; set; }

        #endregion
        #region 인코딩 - Encoding

        /// <summary>
        /// 인코딩
        /// </summary>
        [Display(Name = "인코딩")]
        public string Encoding { get; set; } = "Text";

        #endregion
        #region 홈페이지 - Homepage

        /// <summary>
        /// 홈페이지
        /// </summary>
        public string Homepage { get; set; }

        #endregion
        #region 수정일 - UpdateDate

        /// <summary>
        /// 수정일
        /// </summary>
        public DateTime UpdateDate { get; set; }

        #endregion
        #region 수정 IP - UpdateIP

        /// <summary>
        /// 수정 IP
        /// </summary>
        public string UpdateIP { get; set; }

        #endregion
        #region 파일명 - FileName

        /// <summary>
        /// 파일명
        /// </summary>
        [Display(Name = "파일명")]
        public string FileName { get; set; }

        #endregion
        #region 파일 크기 - FileSize

        /// <summary>
        /// 파일 크기
        /// </summary>
        public int FileSize { get; set; }

        #endregion
        #region 다운로드 수 - DownloadCount

        /// <summary>
        /// 다운로드 수
        /// </summary>
        public int DownloadCount { get; set; }

        #endregion
        #region 참조 ID - ReferenceID

        /// <summary>
        /// 참조 ID
        /// </summary>
        public int ReferenceID { get; set; }

        #endregion
        #region 답변 레벨 - ReplyLevel

        /// <summary>
        /// 답변 레벨
        /// </summary>
        public int ReplyLevel { get; set; }

        #endregion
        #region 답변 순서 - ReplyOrder

        /// <summary>
        /// 답변 순서
        /// </summary>
        public int ReplyOrder { get; set; }

        #endregion
        #region 답변 수 - ReplyCount

        /// <summary>
        /// 답변 수
        /// </summary>
        public int ReplyCount { get; set; }

        #endregion
        #region 부모 ID - ParentID

        /// <summary>
        /// 부모 ID
        /// </summary>
        public int ParentID { get; set; }

        #endregion
        #region 댓글 수 - CommentCount

        /// <summary>
        /// 댓글 수
        /// </summary>
        public int CommentCount { get; set; }

        #endregion
        #region 카테고리 - Category

        /// <summary>
        /// 카테고리
        /// </summary>
        public string Category { get; set; } = "FREE";

        #endregion
    }
}

 

▶ Models/CommentModel.cs

using System;
using System.ComponentModel.DataAnnotations;

namespace TestProject.Models
{
    /// <summary>
    /// 댓글 모델
    /// </summary>
    public class CommentModel
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region ID - ID

        /// <summary>
        /// ID
        /// </summary>
        public int ID { get; set; }

        #endregion
        #region 게시판 ID - NoticeID

        /// <summary>
        /// 게시판 ID
        /// </summary>
        public int NoticeID { get; set; }

        #endregion
        #region 작성자명 - Name

        /// <summary>
        /// 작성자명
        /// </summary>
        [Required(ErrorMessage = "작성자명을 입력해 주시기 바랍니다.")]
        public string Name { get; set; }

        #endregion
        #region 댓글 - Comment

        /// <summary>
        /// 댓글
        /// </summary>
        [Required(ErrorMessage = "댓글을 입력해 주시기 바랍니다.")]
        public string Comment { get; set; }

        #endregion
        #region 작성일 - WriteDate

        /// <summary>
        /// 작성일
        /// </summary>
        public DateTime WriteDate { get; set; }

        #endregion
        #region 패스워드 - Password

        /// <summary>
        /// 패스워드
        /// </summary>
        [Required(ErrorMessage = "패스워드를 입력해 주시기 바랍니다.")]
        public string Password { get; set; }

        #endregion
    }
}

 

▶ Models/NoticeCommentModel.cs

using System.Collections.Generic;

namespace TestProject.Models
{
    /// <summary>
    /// 게시판 댓글 모델
    /// </summary>
    public class NoticeCommentModel
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 게시판 ID - NoticeID

        /// <summary>
        /// 게시판 ID
        /// </summary>
        public int NoticeID { get; set; }

        #endregion
        #region 댓글 리스트 - CommentList

        /// <summary>
        /// 댓글 리스트
        /// </summary>
        public List<CommentModel> CommentList { get; set; }

        #endregion
    }
}

 

▶ Models/INoticeRepository.cs

using System.Collections.Generic;

namespace TestProject.Models
{
    /// <summary>
    /// 게시판 저장소 인터페이스
    /// </summary>
    public interface INoticeRepository
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        #region 게시판 저장하기 - SaveNotice(notice, formType)

        /// <summary>
        /// 게시판 저장하기
        /// </summary>
        /// <param name="notice">게시판</param>
        /// <param name="formType">폼 타입</param>
        /// <returns>처리 레코드 수</returns>
        int SaveNotice(NoticeModel notice, BoardWriteFormType formType);

        #endregion
        #region 게시판 쓰기 - WriteNotice(notice)

        /// <summary>
        /// 게시판 쓰기
        /// </summary>
        /// <param name="notice">게시판</param>
        void WriteNotice(NoticeModel notice);

        #endregion
        #region 게시판 수정하기 - UpdateNotice(notice)

        /// <summary>
        /// 게시판 수정하기
        /// </summary>
        /// <param name="notice">게시판</param>
        /// <returns>처리 레코드 수</returns>
        int UpdateNotice(NoticeModel notice);

        #endregion
        #region 게시판 답변하기 - ReplyNotice(notice)

        /// <summary>
        /// 게시판 답변하기
        /// </summary>
        /// <param name="notice">게시판</param>
        void ReplyNotice(NoticeModel notice);

        #endregion
        #region 게시판 삭제하기 - DeleteNotice(id, password)

        /// <summary>
        /// 게시판 삭제하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <param name="password">패스워드</param>
        /// <returns>처리 레코드 수</returns>
        int DeleteNotice(int id, string password);

        #endregion
        #region 카운트 구하기 - GetCount()

        /// <summary>
        /// 카운트 구하기
        /// </summary>
        int GetCount();

        #endregion
        #region  카운트 구하기 - GetCount(searchField, searchQuery)

        /// <summary>
        /// 카운트 구하기
        /// </summary>
        /// <param name="searchField">검색 필드</param>
        /// <param name="searchQuery">검색 쿼리</param>
        /// <returns>카운트</returns>
        int GetCount(string searchField, string searchQuery);

        #endregion
        #region 게시판 리스트 구하기 - GetNoticeList(int pageIndex)

        /// <summary>
        /// 게시판 리스트 구하기
        /// </summary>
        /// <param name="pageIndex">페이지 인덱스</param>
        List<NoticeModel> GetNoticeList(int pageIndex);

        #endregion
        #region 게시판 리스트 구하기 - GetNoticeList(pageIndex, searchField, searchQuery)

        /// <summary>
        /// 게시판 리스트 구하기
        /// </summary>
        /// <param name="pageIndex">페이지 인덱스</param>
        /// <param name="searchField">검색 필드</param>
        /// <param name="searchQuery">검색 쿼리</param>
        /// <returns>게시판 리스트</returns>
        List<NoticeModel> GetNoticeList(int pageIndex, string searchField, string searchQuery);

        #endregion
        #region 게시판 보기 - GetNotice(id)

        /// <summary>
        /// 게시판 보기
        /// </summary>
        NoticeModel GetNotice(int id);

        #endregion
        #region 파일명 구하기 - GetFileName(id)

        /// <summary>
        /// 파일명 구하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>파일명</returns>
        string GetFileName(int id);

        #endregion
        #region 다운로드 카운트 수정하기 - UpdateDownloadCount(id)

        /// <summary>
        /// 다운로드 카운트 수정하기
        /// </summary>
        /// <param name="id">ID</param>
        void UpdateDownloadCount(int id);

        #endregion
        #region 다운로드 카운트 수정하기 - UpdateDownloadCount(fileName)

        /// <summary>
        /// 다운로드 카운트 수정하기
        /// </summary>
        /// <param name="fileName">파일명</param>
        void UpdateDownloadCount(string fileName);

        #endregion

        #region 사진이 있는 최근 게시판 리스트 구하기 - GetRecentPhotoNoticeList()

        /// <summary>
        /// 사진이 있는 최근 게시판 리스트 구하기
        /// </summary>
        /// <returns>사진이 있는 최근 게시판 리스트</returns>
        List<NoticeModel> GetRecentPhotoNoticeList();

        #endregion
        #region 특정 카테고리 최근 게시판 리스트 구하기 - GetRecentCategoryNoticeList(category)

        /// <summary>
        /// 특정 카테고리 최근 게시판 리스트 구하기
        /// </summary>
        /// <returns>특정 카테고리 최근 게시판 리스트</returns>
        List<NoticeModel> GetRecentCategoryNoticeList(string category);

        #endregion
        #region 최근 게시판 리스트 구하기 - GetRecentNoticeList()

        /// <summary>
        /// 최근 게시판 리스트 구하기
        /// </summary>
        /// <returns>최근 게시판 리스트</returns>
        List<NoticeModel> GetRecentNoticeList();

        #endregion
        #region 최근 게시판 리스트 구하기 - GetRecentNoticeList(noticeCount)

        /// <summary>
        /// 최근 게시판 리스트 구하기
        /// </summary>
        /// <param name="noticeCount">게시판 수</param>
        /// <returns>최근 게시판 리스트</returns>
        List<NoticeModel> GetRecentNoticeList(int noticeCount);

        #endregion
        #region 게시판 고정하기 - PinNotice(id)

        /// <summary>
        /// 게시판 고정하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <remarks>특정 게시글을 공지사항으로 올리기</remarks>
        void PinNotice(int id);

        #endregion
    }
}

 

▶ Models/NoticeRepository.cs

using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;

using Dapper;

namespace TestProject.Models
{
    /// <summary>
    /// 게시판 저장소
    /// </summary>
    public class NoticeRepository : INoticeRepository
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 구성
        /// </summary>
        private IConfiguration configuration;

        /// <summary>
        /// 로그 기록기
        /// </summary>
        private ILogger<NoticeRepository> logger;

        /// <summary>
        /// 연결
        /// </summary>
        private SqlConnection connection;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - NoticeRepository(config, logger)

        /// <summary>
        /// 생성자
        /// </summary>
        public NoticeRepository(IConfiguration config, ILogger<NoticeRepository> logger)
        {
            this.configuration = config;

            this.connection = new SqlConnection
            (
                this.configuration.GetSection("ConnectionStrings").GetSection("DefaultConnection").Value
            );

            this.logger = logger;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 게시판 저장하기 - SaveNotice(notice, formType)

        /// <summary>
        /// 게시판 저장하기
        /// </summary>
        /// <param name="notice">게시판</param>
        /// <param name="formType">폼 타입</param>
        /// <returns>처리 레코드 수</returns>
        public int SaveNotice(NoticeModel notice, BoardWriteFormType formType)
        {
            int recordCount = 0;

            DynamicParameters dynamicParameters = new DynamicParameters();

            dynamicParameters.Add("@Name"       , value : notice.Name       , dbType : DbType.String);
            dynamicParameters.Add("@MailAddress", value : notice.MailAddress, dbType : DbType.String);
            dynamicParameters.Add("@Title"      , value : notice.Title      , dbType : DbType.String);
            dynamicParameters.Add("@Content"    , value : notice.Content    , dbType : DbType.String);
            dynamicParameters.Add("@Password"   , value : notice.Password   , dbType : DbType.String);
            dynamicParameters.Add("@Encoding"   , value : notice.Encoding   , dbType : DbType.String);
            dynamicParameters.Add("@Homepage"   , value : notice.Homepage   , dbType : DbType.String);
            dynamicParameters.Add("@FileName"   , value : notice.FileName   , dbType : DbType.String);
            dynamicParameters.Add("@FileSize"   , value : notice.FileSize   , dbType : DbType.Int32 );

            switch(formType)
            {
                case BoardWriteFormType.Write :

                    dynamicParameters.Add("@WriteIP", value: notice.WriteIP, dbType : DbType.String);

                    recordCount = this.connection.Execute
                    (
                        "WriteNotice",
                        dynamicParameters,
                        commandType : CommandType.StoredProcedure
                    );

                    break;

                case BoardWriteFormType.Modify :

                    dynamicParameters.Add("@UpdateIP", value : notice.UpdateIP, dbType : DbType.String);
                    dynamicParameters.Add("@ID"      , value : notice.ID      , dbType : DbType.Int32 );

                    recordCount = this.connection.Execute
                    (
                        "UpdateNotice",
                        dynamicParameters,
                        commandType : CommandType.StoredProcedure
                    );

                    break;

                case BoardWriteFormType.Reply :

                    dynamicParameters.Add("@WriteIP" , value : notice.WriteIP , dbType : DbType.String);
                    dynamicParameters.Add("@ParentID", value : notice.ParentID, dbType : DbType.Int32 );

                    recordCount = this.connection.Execute
                    (
                        "ReplyNotice",
                        dynamicParameters,
                        commandType : CommandType.StoredProcedure
                    );

                    break;
            }

            return recordCount;
        }

        #endregion
        #region 게시판 쓰기 - WriteNotice(notice)

        /// <summary>
        /// 게시판 쓰기
        /// </summary>
        /// <param name="notice">게시판</param>
        public void WriteNotice(NoticeModel notice)
        {
            this.logger.LogInformation("EXECITE WRITE NOTICE");

            try
            {
                SaveNotice(notice, BoardWriteFormType.Write);
            }
            catch(Exception exception)
            {
                this.logger.LogError($"ERROR WRITE NOTICE : {exception}");
            }
        }

        #endregion
        #region 게시판 수정하기 - UpdateNotice(notice)

        /// <summary>
        /// 게시판 수정하기
        /// </summary>
        /// <param name="notice">게시판</param>
        /// <returns>처리 레코드 수</returns>
        public int UpdateNotice(NoticeModel notice)
        {
            int recordCount = 0;

            this.logger.LogInformation("EXECUTE UPDATE NOTICE");

            try
            {
                recordCount = SaveNotice(notice, BoardWriteFormType.Modify);
            }
            catch(Exception exception)
            {
                this.logger.LogError($"ERROR EXECUTE UPDATE NOTICE {exception}");
            }

            return recordCount;
        }

        #endregion
        #region 게시판 답변하기 - ReplyNotice(notice)

        /// <summary>
        /// 게시판 답변하기
        /// </summary>
        /// <param name="notice">게시판</param>
        public void ReplyNotice(NoticeModel notice)
        {
            this.logger.LogInformation("EXECUTE REPLY NOTICE");

            try
            {
                SaveNotice(notice, BoardWriteFormType.Reply);
            }
            catch(Exception exception)
            {
                this.logger.LogError($"ERROR REPLY NOTICE {exception}");
            }
        }

        #endregion
        #region 게시판 삭제하기 - DeleteNotice(id, password)

        /// <summary>
        /// 게시판 삭제하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <param name="password">패스워드</param>
        /// <returns>처리 레코드 수</returns>
        public int DeleteNotice(int id, string password)
        {
            return this.connection.Execute
            (
                "DeleteNotice",
                new { ID = id, Password = password },
                commandType : CommandType.StoredProcedure
            );
        }

        #endregion
        #region 카운트 구하기 - GetCount()

        /// <summary>
        /// 카운트 구하기
        /// </summary>
        public int GetCount()
        {
            this.logger.LogInformation("EXECUTE GET COUNT");

            try
            {
                return this.connection.Query<int>("SELECT COUNT(*) FROM dbo.Notice").SingleOrDefault();
            }
            catch(Exception exception)
            {
                this.logger.LogError($"ERROR GET COUNT : {exception}");

                return -1;
            }
        }

        #endregion
        #region  카운트 구하기 - GetCount(searchField, searchQuery)

        /// <summary>
        /// 카운트 구하기
        /// </summary>
        /// <param name="searchField">검색 필드</param>
        /// <param name="searchQuery">검색 쿼리</param>
        /// <returns>카운트</returns>
        public int GetCount(string searchField, string searchQuery)
        {
            this.logger.LogInformation("EXECUTE GET COUNT (SEARCH FIELD, SEARCH QUERY)");

            try
            {
                return this.connection.Query<int>
                (
                    "SearchNoticeCount",
                    new { SearchField = searchField, SearchQuery = searchQuery },
                    commandType : CommandType.StoredProcedure
                )
                .SingleOrDefault();
            }
            catch(Exception exception)
            {
                this.logger.LogError($"ERROR GET COUNT (SEARCH FIELD, SEARCH QUERY) : {exception}");

                return -1;
            }
        }

        #endregion
        #region 게시판 리스트 구하기 - GetNoticeList(int pageIndex)

        /// <summary>
        /// 게시판 리스트 구하기
        /// </summary>
        /// <param name="pageIndex">페이지 인덱스</param>
        public List<NoticeModel> GetNoticeList(int pageIndex)
        {
            this.logger.LogInformation("EXECUTE GET NOTICE LIST");

            try
            {
                DynamicParameters dynamicParameters = new DynamicParameters(new { Page = pageIndex });

                return this.connection.Query<NoticeModel>
                (
                    "ListNotice",
                    dynamicParameters,
                    commandType : CommandType.StoredProcedure
                )
                .ToList();
            }
            catch(Exception exception)
            {
                this.logger.LogError($"ERROR GET NOTICE LIST : {exception}");

                return null;
            }
        }

        #endregion
        #region 게시판 리스트 구하기 - GetNoticeList(pageIndex, searchField, searchQuery)

        /// <summary>
        /// 게시판 리스트 구하기
        /// </summary>
        /// <param name="pageIndex">페이지 인덱스</param>
        /// <param name="searchField">검색 필드</param>
        /// <param name="searchQuery">검색 쿼리</param>
        /// <returns>게시판 리스트</returns>
        public List<NoticeModel> GetNoticeList(int pageIndex, string searchField, string searchQuery)
        {
            DynamicParameters dynamicParameters = new DynamicParameters
            (
                new
                {
                    Page        = pageIndex,
                    SearchField = searchField,
                    SearchQuery = searchQuery
                }
            );

            return this.connection.Query<NoticeModel>
            (
                "SearchNotice",
                dynamicParameters, commandType: CommandType.StoredProcedure).ToList();
        }

        #endregion
        #region 게시판 보기 - GetNotice(id)

        /// <summary>
        /// 게시판 보기
        /// </summary>
        public NoticeModel GetNotice(int id)
        {
            DynamicParameters dynamicParameters = new DynamicParameters(new { ID = id });

            return this.connection.Query<NoticeModel>
            (
                "ViewNotice",
                dynamicParameters,
                commandType : CommandType.StoredProcedure
            )
            .SingleOrDefault();
        }

        #endregion
        #region 파일명 구하기 - GetFileName(id)

        /// <summary>
        /// 파일명 구하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>파일명</returns>
        public string GetFileName(int id)
        {
            return this.connection.Query<string>
            (
                "SELECT FileName FROM dbo.Notice WHERE ID = @ID",
                new { ID = id }
            )
            .SingleOrDefault();
        }

        #endregion
        #region 다운로드 카운트 수정하기 - UpdateDownloadCount(id)

        /// <summary>
        /// 다운로드 카운트 수정하기
        /// </summary>
        /// <param name="id">ID</param>
        public void UpdateDownloadCount(int id)
        {
            DynamicParameters dynamicParameters = new DynamicParameters(new { ID = id });

            this.connection.Execute
            (
                "UPDATE dbo.Notice SET DownloadCount = DownloadCount + 1 WHERE ID = @ID",
                dynamicParameters,
                commandType : CommandType.Text
            );
        }

        #endregion
        #region 다운로드 카운트 수정하기 - UpdateDownloadCount(fileName)

        /// <summary>
        /// 다운로드 카운트 수정하기
        /// </summary>
        /// <param name="fileName">파일명</param>
        public void UpdateDownloadCount(string fileName)
        {
            this.connection.Execute
            (
                "UPDATE dbo.Notice SET DownloadCount = DownloadCount + 1 WHERE FileName = @FileName",
                new { FileName = fileName }
            );
        }

        #endregion

        #region 사진이 있는 최근 게시판 리스트 구하기 - GetRecentPhotoNoticeList()

        /// <summary>
        /// 사진이 있는 최근 게시판 리스트 구하기
        /// </summary>
        /// <returns>사진이 있는 최근 게시판 리스트</returns>
        public List<NoticeModel> GetRecentPhotoNoticeList()
        {
            string sql = @"
SELECT TOP 4
    ID
   ,Title
   ,FileName
   ,FileSize
FROM  dbo.Notice
WHERE FileName LIKE '%.png'
OR    FileName LIKE '%.jpg'
OR    FileName LIKE '%.jpeg'
OR    FileName LIKE '%.gif'
ORDER BY ID DESC
";

            return this.connection.Query<NoticeModel>(sql).ToList();
        }

        #endregion
        #region 특정 카테고리 최근 게시판 리스트 구하기 - GetRecentCategoryNoticeList(category)

        /// <summary>
        /// 특정 카테고리 최근 게시판 리스트 구하기
        /// </summary>
        /// <returns>특정 카테고리 최근 게시판 리스트</returns>
        public List<NoticeModel> GetRecentCategoryNoticeList(string category)
        {
            string sql = @"
SELECT TOP 3
    ID
   ,Title
   ,Name
   ,WriteDate
   ,FileName
   ,FileSize
   ,ReadCount
   ,CommentCount
   ,ReplyLevel
FROM  dbo.Notice
WHERE Category = @Category
ORDER BY ID DESC
";

            return this.connection.Query<NoticeModel>(sql, new { Category = category }).ToList();
        }

        #endregion
        #region 최근 게시판 리스트 구하기 - GetRecentNoticeList()

        /// <summary>
        /// 최근 게시판 리스트 구하기
        /// </summary>
        /// <returns>최근 게시판 리스트</returns>
        public List<NoticeModel> GetRecentNoticeList()
        {
            string sql = "SELECT TOP 3 ID, Title, Name, WriteDate FROM dbo.Notice ORDER BY ID DESC";

            return this.connection.Query<NoticeModel>(sql).ToList();
        }

        #endregion
        #region 최근 게시판 리스트 구하기 - GetRecentNoticeList(noticeCount)

        /// <summary>
        /// 최근 게시판 리스트 구하기
        /// </summary>
        /// <param name="noticeCount">게시판 수</param>
        /// <returns>최근 게시판 리스트</returns>
        public List<NoticeModel> GetRecentNoticeList(int noticeCount)
        {
            string sql = $"SELECT TOP {noticeCount} ID, Title, Name, WriteDate FROM dbo.Notice ORDER BY ID DESC";

            return this.connection.Query<NoticeModel>(sql).ToList();
        }

        #endregion
        #region 게시판 고정하기 - PinNotice(id)

        /// <summary>
        /// 게시판 고정하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <remarks>특정 게시글을 공지사항으로 올리기</remarks>
        public void PinNotice(int id)
        {
            this.connection.Execute
            (
                "UPDATE dbo.Notice SET CATEGORY = 'Notice' WHERE ID = @ID",
                new { ID = id }
            );
        }

        #endregion
    }
}

 

▶ Models/ICommentRepository.cs

using System.Collections.Generic;

namespace TestProject.Models
{
    /// <summary>
    /// 댓글 저장소
    /// </summary>
    public interface ICommentRepository
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method

        #region 댓글 추가하기 - WriteComment(comment)

        /// <summary>
        /// 댓글 추가하기
        /// </summary>
        /// <param name="comment">댓글</param>
        void WriteComment(CommentModel comment);

        #endregion
        #region 댓글 리스트 구하기 - GetCommentList(noticeID)

        /// <summary>
        /// 댓글 리스트 구하기
        /// </summary>
        /// <param name="noticeID">게시판 ID</param>
        /// <returns>댓글 리스트</returns>
        List<CommentModel> GetCommentList(int noticeID);

        #endregion
        #region 카운트 구하기 - GetCount(noticeID, id, password)

        /// <summary>
        /// 카운트 구하기
        /// </summary>
        /// <param name="noticeID">게시판 ID</param>
        /// <param name="id">ID</param>
        /// <param name="password">패스워드</param>
        /// <returns>카운트</returns>
        int GetCount(int noticeID, int id, string password);

        #endregion
        #region 댓글 삭제하기 - DeleteComment(noticeID, id, password)

        /// <summary>
        /// 댓글 삭제하기
        /// </summary>
        /// <param name="noticeID">게시판 ID</param>
        /// <param name="id">ID</param>
        /// <param name="password">패스워드</param>
        int DeleteComment(int noticeID, int id, string password);

        #endregion
        #region 최근 댓글 리스트 구하기 - GetRecentCommentList()

        /// <summary>
        /// 최근 댓글 리스트 구하기
        /// </summary>
        /// <returns>최근 댓글 리스트</returns>
        List<CommentModel> GetRecentCommentList();

        #endregion
    }
}

 

▶ Models/CommentRepository.cs

using System.Collections.Generic;
using System.Data;
using System.Data.SqlClient;
using System.Linq;

using Dapper;

namespace TestProject.Models
{
    /// <summary>
    /// 댓글 저장소
    /// </summary>
    public class CommentRepository : ICommentRepository
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 연결
        /// </summary>
        private SqlConnection connection;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - CommentRepository(connectionString)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="connectionString">연결 문자열</param>
        public CommentRepository(string connectionString)
        {
            this.connection = new SqlConnection(connectionString);
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 댓글 추가하기 - WriteComment(comment)

        /// <summary>
        /// 댓글 추가하기
        /// </summary>
        /// <param name="comment">댓글</param>
        public void WriteComment(CommentModel comment)
        {
            DynamicParameters dynamicParameters = new DynamicParameters();

            dynamicParameters.Add("@NoticeID", value : comment.NoticeID, dbType : DbType.Int32 );
            dynamicParameters.Add("@Name"    , value : comment.Name    , dbType : DbType.String);
            dynamicParameters.Add("@Comment" , value : comment.Comment , dbType : DbType.String);
            dynamicParameters.Add("@Password", value : comment.Password, dbType : DbType.String);

            string sql = @"
INSERT INTO dbo.NoticeComment
(
    NoticeID,
    [Name],
    Comment,
    Password
)
Values
(
    @NoticeID,
    @Name,
    @Comment,
    @Password
);

UPDATE dbo.Notice
SET    CommentCount = CommentCount + 1 
Where  ID = @NoticeID
";

            this.connection.Execute(sql, dynamicParameters, commandType : CommandType.Text);
        }

        #endregion
        #region 댓글 리스트 구하기 - GetCommentList(noticeID)

        /// <summary>
        /// 댓글 리스트 구하기
        /// </summary>
        /// <param name="noticeID">게시판 ID</param>
        /// <returns>댓글 리스트</returns>
        public List<CommentModel> GetCommentList(int noticeID)
        {
            return this.connection.Query<CommentModel>
            (
                "SELECT * FROM dbo.NoticeComment WHERE NoticeID = @NoticeID",
                new { NoticeID = noticeID },
                commandType : CommandType.Text
            )
            .ToList();
        }

        #endregion
        #region 카운트 구하기 - GetCount(noticeID, id, password)

        /// <summary>
        /// 카운트 구하기
        /// </summary>
        /// <param name="noticeID">게시판 ID</param>
        /// <param name="id">ID</param>
        /// <param name="password">패스워드</param>
        /// <returns>카운트</returns>
        public int GetCount(int noticeID, int id, string password)
        {
            return this.connection.Query<int>
            (
                "SELECT COUNT(*) FROM dbo.NoticeComment WHERE NoticeID = @NoticeID AND ID = @ID AND Password = @Password",
                new { NoticeID = noticeID, ID = id, Password = password },
                commandType : CommandType.Text
            )
            .SingleOrDefault();
        }

        #endregion
        #region 댓글 삭제하기 - DeleteComment(noticeID, id, password)

        /// <summary>
        /// 댓글 삭제하기
        /// </summary>
        /// <param name="noticeID">게시판 ID</param>
        /// <param name="id">ID</param>
        /// <param name="password">패스워드</param>
        public int DeleteComment(int noticeID, int id, string password)
        {
            return this.connection.Execute
            (
                @"
DELETE dbo.NoticeComment
WHERE  NoticeID = @NoticeID
And    ID       = @ID
And    Password = @Password;

UPDATE dbo.Notice
SET    CommentCount = CommentCount - 1 
WHERE  ID = @NoticeID",
                new { NoticeID = noticeID, Id = id, Password = password },
                commandType : CommandType.Text
            );
        }

        #endregion
        #region 최근 댓글 리스트 구하기 - GetRecentCommentList()

        /// <summary>
        /// 최근 댓글 리스트 구하기
        /// </summary>
        /// <returns>최근 댓글 리스트</returns>
        public List<CommentModel> GetRecentCommentList()
        {
            string sql = "SELECT TOP 3 ID, NoticeID, Comment, WriteDate FROM dbo.NoticeComment ORDER BY ID DESC";

            return this.connection.Query<CommentModel>(sql).ToList();
        }

        #endregion
    }
}

 

▶ Controllers/NoticeBoardController.cs

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.IO;
using System.Net.Http.Headers;
using System.Threading.Tasks;

using TestProject.Models;

namespace TestProject.Controllers
{
    /// <summary>
    /// 게시판 컨트롤러
    /// </summary>
    public class NoticeBoardController : Controller
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Field
        ////////////////////////////////////////////////////////////////////////////////////////// Private

        #region Field

        /// <summary>
        /// 환경 변수
        /// </summary>
        private IWebHostEnvironment environment;

        /// <summary>
        /// 게시판 저장소
        /// </summary>
        private INoticeRepository noticeRepository;

        /// <summary>
        /// 댓글 저장소
        /// </summary>
        private ICommentRepository commentRepository;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 검색 모드 - SearchMode

        /// <summary>
        /// 검색 모드
        /// </summary>
        public bool SearchMode { get; set; } = false;

        #endregion
        #region 검색 필드 - SearchField

        /// <summary>
        /// 검색 필드
        /// </summary>
        public string SearchField { get; set; }

        #endregion
        #region 검색 쿼리 - SearchQuery

        /// <summary>
        /// 검색 쿼리
        /// </summary>
        public string SearchQuery { get; set; }

        #endregion

        #region 페이지 인덱스 - PageIndex

        /// <summary>
        /// 페이지 인덱스
        /// </summary>
        public int PageIndex { get; set; } = 0;

        #endregion
        #region 레코드 카운트 - RecordCount

        /// <summary>
        /// 레코드 카운트
        /// </summary>
        public int RecordCount { get; set; } = 0;

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - NoticeBoardController(environment, noticeRepository, commentRepository)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="environment">환경</param>
        /// <param name="noticeRepository">게시판 저장소</param>
        /// <param name="commentRepository">댓글 저장소</param>
        public NoticeBoardController(IWebHostEnvironment environment, INoticeRepository noticeRepository, ICommentRepository commentRepository)
        {
            this.environment       = environment;
            this.noticeRepository  = noticeRepository;
            this.commentRepository = commentRepository;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 인덱스 페이지 처리하기 - Index()

        /// <summary>
        /// 인덱스 페이지 처리하기
        /// </summary>
        /// <returns>액션 결과</returns>
        public IActionResult Index()
        {
            SearchMode = (!string.IsNullOrEmpty(Request.Query["SearchField"]) && !string.IsNullOrEmpty(Request.Query["SearchQuery"]));

            if(SearchMode)
            {
                SearchField = Request.Query["SearchField"].ToString();
                SearchQuery = Request.Query["SearchQuery"].ToString();
            }

            if(!string.IsNullOrEmpty(Request.Query["Page"].ToString()))
            {
                PageIndex = Convert.ToInt32(Request.Query["Page"]) - 1;
            }

            if(!string.IsNullOrEmpty(Request.Cookies["TestProjectPageNumber"]))
            {
                if(!String.IsNullOrEmpty(Request.Cookies["TestProjectPageNumber"]))
                {
                    PageIndex = Convert.ToInt32(Request.Cookies["TestProjectPageNumber"]);
                }
                else
                {
                    PageIndex = 0;
                }
            }

            IEnumerable<NoticeModel> noticeEnumerable;

            if(!SearchMode)
            {
                RecordCount = this.noticeRepository.GetCount();

                noticeEnumerable = this.noticeRepository.GetNoticeList(PageIndex);
            }
            else
            {
                RecordCount = this.noticeRepository.GetCount(SearchField, SearchQuery);

                noticeEnumerable = this.noticeRepository.GetNoticeList(PageIndex, SearchField, SearchQuery);
            }

            ViewBag.RecordCount = RecordCount;
            ViewBag.SearchMode  = SearchMode;
            ViewBag.SearchField = SearchField;
            ViewBag.SearchQuery = SearchQuery;

            return View(noticeEnumerable);
        }

        #endregion
        #region 생성 페이지 처리하기 - Create()

        /// <summary>
        /// 생성 페이지 처리하기
        /// </summary>
        /// <returns>액션 결과</returns>
        [HttpGet]
        public IActionResult Create()
        {
            ViewBag.FormType         = BoardWriteFormType.Write;
            ViewBag.TitleDescription = "게시글 쓰기 - 다음 항목들을 입력해 주시기 바랍니다.";
            ViewBag.SaveButtonText   = "저장";

            return View();
        }

        #endregion
        #region 생성 페이지 처리하기 - Create(sourceNotice, formFileCollection)

        /// <summary>
        /// 생성 페이지 처리하기
        /// </summary>
        /// <param name="sourceNotice">소스 게시글</param>
        /// <param name="formFileCollection">폼 파일 컬렉션</param>
        /// <returns>액션 결과</returns>
        [HttpPost]
        public async Task<IActionResult> Create(NoticeModel sourceNotice, ICollection<IFormFile> formFileCollection)
        {
            string fileName = string.Empty;
            int    fileSize = 0;

            string uploadDirectoryPath = Path.Combine(this.environment.WebRootPath, "upload");

            foreach(IFormFile formFile in formFileCollection)
            {
                if(formFile.Length > 0)
                {
                    fileSize = Convert.ToInt32(formFile.Length);

                    fileName = FileHelper.GetUniqueFileName
                    (
                        uploadDirectoryPath,
                        Path.GetFileName(ContentDispositionHeaderValue.Parse(formFile.ContentDisposition).FileName.Trim('"'))
                    );

                    using(FileStream fileStream = new FileStream(Path.Combine(uploadDirectoryPath, fileName), FileMode.Create))
                    {
                        await formFile.CopyToAsync(fileStream);
                    }
                }
            }

            NoticeModel targetNotice = new NoticeModel();

            targetNotice.Name        = sourceNotice.Name;
            targetNotice.MailAddress = HTMLHelper.Encode(sourceNotice.MailAddress);
            targetNotice.Homepage    = sourceNotice.Homepage;
            targetNotice.Title       = HTMLHelper.Encode(sourceNotice.Title);
            targetNotice.Content     = sourceNotice.Content;
            targetNotice.FileName    = fileName;
            targetNotice.FileSize    = fileSize;
            targetNotice.Password    = sourceNotice.Password;
            targetNotice.WriteIP     = HttpContext.Connection.RemoteIpAddress.ToString();
            targetNotice.Encoding    = sourceNotice.Encoding;

            this.noticeRepository.WriteNotice(targetNotice);

            TempData["Message"] = "게시글이 생성되었습니다.";

            return RedirectToAction("Index");
        }

        #endregion
        #region 수정 페이지 처리하기 - Update(id)

        /// <summary>
        /// 수정 페이지 처리하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>액션 결과</returns>
        [HttpGet]
        public IActionResult Update(int id)
        {
            ViewBag.FormType         = BoardWriteFormType.Modify;
            ViewBag.TitleDescription = "게시글 수정 - 다음 항목들을 수정해 주시기 바랍니다.";
            ViewBag.SaveButtonText   = "수정";

            NoticeModel notice = this.noticeRepository.GetNotice(id);

            if(notice.FileName.Length > 1)
            {
                ViewBag.FileName         = notice.FileName;
                ViewBag.FileSize         = notice.FileSize;
                ViewBag.FileNamePrevious = $"기존 업로드 파일명 : {notice.FileName}";
            }
            else
            {
                ViewBag.FileName = string.Empty;
                ViewBag.FileSize = 0;
            }

            return View(notice);
        }

        #endregion
        #region 수정 페이지 처리하기 - Update(sourceNotice, formFileCollection, id, previousFileName, previousFileSize)

        /// <summary>
        /// 수정 페이지 처리하기
        /// </summary>
        /// <param name="sourceNotice">소스 게시글</param>
        /// <param name="formFileCollection">폼 파일 컬렉션</param>
        /// <param name="id">ID</param>
        /// <param name="previousFileName">이전 파일명</param>
        /// <param name="previousFileSize">이전 파일 크기</param>
        /// <returns></returns>
        [HttpPost]
        public async Task<IActionResult> Update
        (
            NoticeModel            sourceNotice,
            ICollection<IFormFile> formFileCollection,
            int                    id,
            string                 previousFileName = "",
            int                    previousFileSize = 0
        )
        {
            ViewBag.FormType         = BoardWriteFormType.Modify;
            ViewBag.TitleDescription = "게시글 수정 - 아래 항목을 수정해 주시기 바랍니다.";
            ViewBag.SaveButtonText   = "수정";

            string fileName = string.Empty;
            int    fileSize = 0;

            if(previousFileName != null)
            {
                fileName = previousFileName;
                fileSize = previousFileSize;
            }

            string uploadDirectoryPath = Path.Combine(this.environment.WebRootPath, "upload");

            foreach(IFormFile formFile in formFileCollection)
            {
                if(formFile.Length > 0)
                {
                    fileSize = Convert.ToInt32(formFile.Length);

                    fileName = FileHelper.GetUniqueFileName
                    (
                        uploadDirectoryPath,
                        Path.GetFileName(ContentDispositionHeaderValue.Parse(formFile.ContentDisposition).FileName.Trim('"'))
                    );

                    using(FileStream fileStream = new FileStream(Path.Combine(uploadDirectoryPath, fileName), FileMode.Create))
                    {
                        await formFile.CopyToAsync(fileStream);
                    }
                }
            }
            
            NoticeModel targetNotice = new NoticeModel();

            targetNotice.ID          = id;
            targetNotice.Name        = sourceNotice.Name;
            targetNotice.MailAddress = HTMLHelper.Encode(sourceNotice.MailAddress);
            targetNotice.Homepage    = sourceNotice.Homepage;
            targetNotice.Title       = HTMLHelper.Encode(sourceNotice.Title);
            targetNotice.Content     = sourceNotice.Content;
            targetNotice.FileName    = fileName;
            targetNotice.FileSize    = fileSize;
            targetNotice.Password    = sourceNotice.Password;
            targetNotice.UpdateIP    = HttpContext.Connection.RemoteIpAddress.ToString();
            targetNotice.Encoding    = sourceNotice.Encoding;

            int recordCount = this.noticeRepository.UpdateNotice(targetNotice);

            if(recordCount > 0)
            {
                TempData["Message"] = "게시글 수정이 완료되었습니다.";

                return RedirectToAction("Details", new { ID = id });
            }
            else
            {
                ViewBag.ErrorMessage = "게시글 수정을 실패했습니다. 패스워드를 확인해 주시기 바랍니다.";

                return View(targetNotice);
            }
        }

        #endregion
        #region 삭제 페이지 처리하기 - Delete(id)

        /// <summary>
        /// 삭제 페이지 처리하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>액션 결과</returns>
        [HttpGet]
        public IActionResult Delete(int id)
        {
            ViewBag.ID = id;

            return View();
        }

        #endregion
        #region 삭제 페이지 처리하기 - Delete(id, password)

        /// <summary>
        /// 삭제 페이지 처리하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <param name="password">패스워드</param>
        /// <returns>액션 결과</returns>
        [HttpPost]
        public IActionResult Delete(int id, string password)
        {
            if(this.noticeRepository.DeleteNotice(id, password) > 0)
            {
                TempData["Message"] = "게시글이 삭제되었습니다.";

                //return RedirectToAction("DeleteCompleted");
                return RedirectToAction("Index");
            }
            else
            {
                ViewBag.Message = "게시글 삭제가 실패했습니다. 패스워드를 확인해 주시기 바랍니다.";
            }

            ViewBag.ID = id;

            return View();
        }

        #endregion
        #region 삭제 완료시 페이지 처리하기 - DeleteCompleted()

        /// <summary>
        /// 삭제 완료시 페이지 처리하기
        /// </summary>
        /// <returns>액션 결과</returns>
        public IActionResult DeleteCompleted()
        {
            return View();
        }

        #endregion
        #region 상세 페이지 처리하기 - Details(id)

        /// <summary>
        /// 상세 페이지 처리하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>액션 결과</returns>
        public IActionResult Details(int id)
        {
            NoticeModel notice = this.noticeRepository.GetNotice(id);

            ContentEncodingType encodingType = (ContentEncodingType)Enum.Parse(typeof(ContentEncodingType), notice.Encoding);

            string targetContent = string.Empty;

            switch(encodingType)
            {
                case ContentEncodingType.Text :

                    targetContent = HTMLHelper.EncodeIncludingTabAndSpace(notice.Content);

                    break;

                case ContentEncodingType.HTML :

                    targetContent = notice.Content;

                    break;

                case ContentEncodingType.Mixed :

                    targetContent = notice.Content.Replace("\r\n", "<br />");

                    break;

                default :

                    targetContent = notice.Content;

                    break;
            }

            ViewBag.Content = targetContent;
            
            if(notice.FileName.Length > 1)
            {
                ViewBag.FileName = String.Format
                (
                    "<a href='/NoticeBoard/Download?id={0}'>{1} {2} / 전송 수 : {3}</a>",
                    notice.ID,
                    "<img src=\"/Image/FileExtension/ext_zip.gif\" border=\"0\">",
                    notice.FileName,
                    notice.DownloadCount
                );

                if(NoticeHelper.IsImageFile(notice.FileName))
                {
                    ViewBag.ImageDownloadURL = $"<img src=\'/NoticeBoard/DownloadImage/{notice.ID}\'><br />";
                }
            }
            else
            {
                ViewBag.FileName = "(업로드된 파일이 없습니다.)";
            }

            NoticeCommentModel noticeComment = new NoticeCommentModel();

            noticeComment.CommentList = this.commentRepository.GetCommentList(notice.ID);
            noticeComment.NoticeID    = notice.ID;

            ViewBag.NoticeComment = noticeComment;

            return View(notice);
        }

        #endregion
        #region 다운로드 페이지 처리하기 - Download(id)

        /// <summary>
        /// 다운로드 페이지 처리하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>파일 결과</returns>
        public FileResult Download(int id)
        {
            string fileName = string.Empty;
            
            fileName = this.noticeRepository.GetFileName(id);

            if(fileName == null)
            {
                return null;
            }
            else
            {
                this.noticeRepository.UpdateDownloadCount(id);

                byte[] fileByteArray = System.IO.File.ReadAllBytes
                (
                    Path.Combine
                    (
                        this.environment.WebRootPath,
                        "upload",
                        fileName
                    )
                );

                return File(fileByteArray, "application/octet-stream", fileName);
            }
        }

        #endregion
        #region 이미지 다운로드 페이지 처리하기 - DownloadImage(id)

        /// <summary>
        /// 이미지 다운로드 페이지 처리하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>파일 결과</returns>
        public FileResult DownloadImage(int id)
        {
            string fileName = string.Empty;

            fileName = this.noticeRepository.GetFileName(id);

            if(fileName == null)
            {
                return null;
            }
            else
            {
                string fileExtension = Path.GetExtension(fileName);
                string contentType   = string.Empty;

                if(fileExtension == ".gif" || fileExtension == ".jpg" || fileExtension == ".jpeg" || fileExtension == ".png")
                {
                    switch(fileExtension)
                    {
                        case ".gif"  : contentType = "image/gif";  break;
                        case ".jpg"  : contentType = "image/jpeg"; break;
                        case ".jpeg" : contentType = "image/jpeg"; break;
                        case ".png"  : contentType = "image/png";  break;
                    }
                }

                this.noticeRepository.UpdateDownloadCount(fileName);

                byte[] fileByteArray = System.IO.File.ReadAllBytes(Path.Combine(this.environment.WebRootPath, "upload") + "\\" + fileName);

                return File(fileByteArray, contentType, fileName);
            }
        }

        #endregion
        #region 답변 페이지 처리하기 - Reply(id)

        /// <summary>
        /// 답변 페이지 처리하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>액션 결과</returns>
        [HttpGet]
        public IActionResult Reply(int id)
        {
            ViewBag.FormType         = BoardWriteFormType.Reply;
            ViewBag.TitleDescription = "글 답변 - 다음 항목을 입력해 주시기 바랍니다.";
            ViewBag.SaveButtonText   = "답변";

            NoticeModel notice = this.noticeRepository.GetNotice(id);

            NoticeModel newNotice = new NoticeModel();

            newNotice.Title   = $"Re : {notice.Title}";
            newNotice.Content = $"\n\nOn {notice.WriteDate}, '{notice.Name}' wrote:\n----------\n>" +
                                $"{notice.Content.Replace("\n", "\n>")}\n---------";

            return View(newNotice);
        }

        #endregion
        #region 답변 페이지 처리하기 - Reply(sourceNotice, formFileCollection, id)

        /// <summary>
        /// 답변 페이지 처리하기
        /// </summary>
        /// <param name="sourceNotice">게시글</param>
        /// <param name="formFileCollection">폼 파일 컬렉션</param>
        /// <param name="id">ID</param>
        /// <returns>액션 결과</returns>
        [HttpPost]
        public async Task<IActionResult> Reply(NoticeModel sourceNotice, ICollection<IFormFile> formFileCollection, int id)
        {
            string fileName = string.Empty;
            int    fileSize = 0;

            string uploadDicrectoryPath = Path.Combine(this.environment.WebRootPath, "upload");

            foreach(IFormFile formFile in formFileCollection)
            {
                if(formFile.Length > 0)
                {
                    fileSize = Convert.ToInt32(formFile.Length);

                    fileName = FileHelper.GetUniqueFileName
                    (
                        uploadDicrectoryPath,
                        Path.GetFileName(ContentDispositionHeaderValue.Parse(formFile.ContentDisposition).FileName.Trim('"'))
                    );

                    using(FileStream fileStream = new FileStream(Path.Combine(uploadDicrectoryPath, fileName), FileMode.Create))
                    {
                        await formFile.CopyToAsync(fileStream);
                    }
                }
            }

            NoticeModel targetNotice = new NoticeModel();

            targetNotice.ID          = targetNotice.ParentID = Convert.ToInt32(id);
            targetNotice.Name        = sourceNotice.Name;
            targetNotice.MailAddress = HTMLHelper.Encode(sourceNotice.MailAddress);
            targetNotice.Homepage    = sourceNotice.Homepage;
            targetNotice.Title       = HTMLHelper.Encode(sourceNotice.Title);
            targetNotice.Content     = sourceNotice.Content;
            targetNotice.FileName    = fileName;
            targetNotice.FileSize    = fileSize;
            targetNotice.Password    = sourceNotice.Password;
            targetNotice.WriteIP     = HttpContext.Connection.RemoteIpAddress.ToString();
            targetNotice.Encoding    = sourceNotice.Encoding;

            this.noticeRepository.ReplyNotice(targetNotice);

            TempData["Message"] = "답변 글이 저장되었습니다.";

            return RedirectToAction("Index");
        }

        #endregion
        #region 댓글 추가 페이지 처리하기 - AddComment(noticeID, name, password, comment)

        /// <summary>
        /// 댓글 추가 페이지 처리하기
        /// </summary>
        /// <param name="noticeID">게시글 ID</param>
        /// <param name="name">성명</param>
        /// <param name="password">패스워드</param>
        /// <param name="comment">댓글</param>
        /// <returns>액션 결과</returns>
        [HttpPost]
        public IActionResult AddComment(int noticeID, string name, string password, string comment)
        {
            CommentModel newComment = new CommentModel();

            newComment.NoticeID = noticeID;
            newComment.Name     = name;
            newComment.Password = password;
            newComment.Comment  = comment;

            this.commentRepository.WriteComment(newComment);
            
            return RedirectToAction("Details", new { ID = noticeID });
        }

        #endregion
        #region 댓글 삭제 페이지 처리하기 - DeleteComment(noticeID, id)

        /// <summary>
        /// 댓글 삭제 페이지 처리하기
        /// </summary>
        /// <param name="noticeID">게시글 ID</param>
        /// <param name="id">ID</param>
        /// <returns>액션 결과</returns>
        [HttpGet]
        public IActionResult DeleteComment(string noticeID, string id)
        {
            ViewBag.NoticeID = noticeID;
            ViewBag.ID       = id;

            return View();
        }

        #endregion
        #region 댓글 삭제 페이지 처리하기 - DeleteComment(noticeID, id, password)

        /// <summary>
        /// 댓글 삭제 페이지 처리하기
        /// </summary>
        /// <param name="noticeID">게시글 ID</param>
        /// <param name="id">ID</param>
        /// <param name="password">패스워드</param>
        /// <returns>액션 결과</returns>
        [HttpPost]
        public IActionResult DeleteComment(string noticeID, string id, string password)
        {
            if(this.commentRepository.GetCount(Convert.ToInt32(noticeID), Convert.ToInt32(id), password) > 0)
            {
                this.commentRepository.DeleteComment(Convert.ToInt32(noticeID), Convert.ToInt32(id), password);

                return RedirectToAction("Details", new { ID = noticeID });
            }

            ViewBag.NoticeID     = noticeID;
            ViewBag.ID           = id;
            ViewBag.ErrorMessage = "패스워드가 틀립니다. 다시 입력해 주시기 바랍니다.";

            return View();
        }

        #endregion
        #region 게시글 고정 페이지 처리하기 - Pin(id)

        /// <summary>
        /// 게시글 고정 페이지 처리하기
        /// </summary>
        /// <param name="id">ID</param>
        /// <returns>액션 결과</returns>
        [HttpGet]
        [Authorize("Administrators")]
        public IActionResult Pin(int id)
        {
            this.noticeRepository.PinNotice(id); 

            return RedirectToAction("Details", new { ID = id });
        }

        #endregion
        #region 최근 게시글 리스트 페이지 처리하기 - ListRecentNotice()

        /// <summary>
        /// 최근 게시글 리스트 페이지 처리하기
        /// </summary>
        /// <returns>액션 결과</returns>
        public IActionResult ListRecentNotice()
        {
            return View();
        }

        #endregion
        #region 최근 댓글 리스트 페이지 처리하기 - ListRecentComment()

        /// <summary>
        /// 최근 댓글 리스트 페이지 처리하기
        /// </summary>
        /// <returns>액션 결과</returns>
        public IActionResult ListRecentComment()
        {
            return View();
        }

        #endregion
    }
}

 

▶ Views/NoticeBoard/_SearchForm.cshtml

<form asp-controller="NoticeBoard" asp-action="Index" method="get">
    <select name="searchField" id="searchField"
        class="form-control"
        style="width:80px;display:inline-block;">
        <option value="Name">성명</option>
        <option value="Title">제목</option>
        <option value="Content">내용</option>
    </select>
    <input type="text" name="searchQuery" id="searchQuery"
        class="form-control"
        style="width:200px;display:inline-block;"
        value="" />
    <input type="submit" name="searchButton"
        class="form-control"
        style="width:100px;display:inline-block;"
        value="검색" />
</form>

 

▶ Views/NoticeBoard/_BoardEditorForm.cshtml

@model NoticeModel
<div class="form-horizontal">
    @if(ViewBag.FormType == TestProject.Models.BoardWriteFormType.Modify)
    {
        <div class="form-group">
            <label asp-for="ID" class="col-md-2 control-label"></label>
            <div class="col-md-10">
                <input asp-for="ID"
                    class="form-control"
                    style="border:0;width:100px;"
                    maxlength="10"
                    readonly />
                <input type="hidden" name="ID" value="@Model.ID" />
            </div>
        </div>
    }
    <div class="form-group">
        <label asp-for="Name" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="Name"
                class="form-control"
                style="width:150px;"
                maxlength="10" />
            <span asp-validation-for="Name" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group">
        <label asp-for="MailAddress" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="MailAddress"
                class="form-control"
                style="width:200px;display:inline-block;"
                maxlength="80" />
            <span style="color:#aaaaaa;font-style:italic">&nbsp;(선택)</span>
            <span asp-validation-for="MailAddress" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group">
        <label asp-for="Homepage" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="Homepage"
                class="form-control"
                style="width:300px;display:inline-block;"
                maxlength="80" />
            <span style="color:#aaaaaa;font-style:italic">&nbsp;(선택)</span>
            <span asp-validation-for="Homepage" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group">
        <label asp-for="Title" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input asp-for="Title"
                class="form-control"
                style="width:480px;" />
            <span asp-validation-for="Title" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group">
        <label asp-for="Content" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <textarea asp-for="Content"
                class="form-control"
                style="width:480px;height:100px;"
                rows="5"
                cols="80">
            </textarea>
            <span asp-validation-for="Content" class="text-danger"></span>
        </div>
    </div>
    <div class="form-group">
        <label asp-for="FileName" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input type="file" name="formFileCollection"
                style="width:290px;display:inline-block;"
                value="" />
            <span style="color:#aaaaaa;font-style:italic">&nbsp;(선택)</span>
            <span asp-validation-for="FileName" class="text-danger"></span>
            @if(ViewBag.FileSize > 0)
            {
                <br />
                @ViewBag.FileNamePrevious
            }
            <input type="hidden" name="previousFileName" value="@ViewBag.FileName" />
            <input type="hidden" name="previousFileSize" value="@ViewBag.FileSize" />
        </div>
    </div>
    <div class="form-group">
        <label asp-for="Encoding" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <select asp-for="Encoding" class="form-control" style="width:100px;">
                <option value="Text" selected>Text</option>
                <option value="Html">HTML</option>
                <option value="Mixed">Mixed</option>
            </select>
        </div>
    </div>
    <div class="form-group">
        <label asp-for="Password" class="col-md-2 control-label"></label>
        <div class="col-md-10">
            <input type="password" asp-for="Password"
                class="form-control"
                style="width:150px;display:inline-block;"
                maxlength="20" />
            <span style="color:#aaaaaa;">&nbsp;(수정/삭제시 필요)</span><br />
            <span asp-validation-for="Password" class="text-danger"></span>
        </div>
    </div>
    <div class="col-md-offset-2 col-md-10">
        <input type="submit" class="btn btn-primary" value="@ViewBag.SaveButtonText" />
        <a asp-action="Index" class="btn btn-default">리스트</a>
        <div asp-validation-summary="ModelOnly" class="text-danger"></div>
        <span class="text-danger">@ViewBag.ErrorMessage</span>
    </div>
</div>
<input type="hidden" asp-for="Category" value="FREE" />

 

▶ Views/NoticeBoard/_BoardCommentControl.cshtml

@model NoticeCommentModel
<table style="padding:10px;margin-left:20px;margin-right:20px;width:95%;">
    @foreach(CommentModel comment in Model.CommentList)
    {
        <tr style="border-bottom:1px dotted silver;">
            <td style="width:100px;">
                @comment.Name
            </td>
            <td style="width:400px;">
                @Html.Raw(comment.Comment.Replace("\r\n", "<br />"))
            </td>
            <td style="width:200px;">
                @comment.WriteDate
            </td>
            <td style="width:10px;text-align:center;">
                <a href="/NoticeBoard/DeleteComment?noticeID=@Model.NoticeID&id=@comment.ID"
                    title="댓글 삭제">
                    <img border="0" src="/Image/NoticeBoard/delete.gif" />
                </a>
            </td>
        </tr>
    }
</table>
<br />
<script>
    function CheckForm()
    {
        var name     = document.getElementById("name");
        var password = document.getElementById("password");
        var comment  = document.getElementById("comment");

        if(name.value.length < 1 || comment.value.length < 1 || password.value.length < 1)
        {
            window.alert("성명, 패스워드 및 댓들을 입력해 주시기 바랍니다.");

            name.focus();

            return false;
        }

        return true;
    }
</script>
<form asp-controller="NoticeBoard" asp-action="AddComment" method="post" onsubmit="return CheckForm();">
    <input type="hidden" name="noticeID" value="@Model.NoticeID" />
    <table style="width:520px;margin-left:auto;">
        <tr>
            <td style="width:60px;text-align:right;">성명&nbsp;</td>
            <td style="width:100px;">
                <input type="text" name="name" id="name"
                    class="form-control"
                    style="width:100px;display:inline-block;" />
            </td>
            <td style="width:80px;text-align:right;">패스워드&nbsp;</td>
            <td style="width:100px;">
                <input type="password" name="password" id="password"
                    class="form-control"
                    style="width:100px;display:inline-block;" />
            </td>
            <td style="width:180px;text-align:right;">
                <input type="submit"
                    class="form-control btn btn-primary"
                    style="width:120px;display:inline-block;"
                    value="댓글 남기기" />
            </td>
        </tr>
        <tr>
            <td style="width:60px;text-align:right;">댓글&nbsp;</td>
            <td colspan="4" style="width:460px;">
                <textarea name="comment" id="comment"
                    class="form-control"
                    style="width:460px;display:inline-block;"
                    rows="3"
                    cols="70">
                </textarea>
            </td>
        </tr>
    </table>
</form>
<hr />

 

▶ Views/NoticeBoard/Index.cshtml

@using TestProject;
@model IEnumerable<NoticeModel>
@inject INoticeRepository NoticeRepository
@{
    ViewData["Title"] = "게시판 리스트";
}
<h2 style="text-align:center;">게시판</h2>
<span style="color:#ff0000">
    글 목록 - ASP.NET CORE MVC 게시판 입니다.
</span>
<hr />
<table style="width:700px;margin-left:auto;margin-right:auto;">
    <tr>
        <td>
            <div style="font-style:italic;text-align:right;font-size:8pt;">
                Total Record: @ViewBag.RecordCount
            </div>
            <table class="table table-bordered table-hover table-condensed table-striped table-responsive">
                <tr style="background-color:rgb(222,222,222)">
                    <th style="width:50px;text-align:center;">
                        @Html.DisplayNameFor(model => model.ID)
                    </th>
                    <th style="width:300px;text-align:center;">
                        @Html.DisplayNameFor(model => model.Title)
                    </th>
                    <th style="width:80px;text-align:center;">
                        @Html.DisplayNameFor(model => model.FileName)
                    </th>
                    <th style="width:90px;text-align:center;">
                        @Html.DisplayNameFor(model => model.Name)
                    </th>
                    <th style="width:90px;text-align:center;">
                        @Html.DisplayNameFor(model => model.WriteDate)
                    </th>
                    <th style="width:90px;text-align:center;">
                        @Html.DisplayNameFor(model => model.ReadCount)
                    </th>
                </tr>
                <cache>
                    @foreach(NoticeModel notice in NoticeRepository.GetRecentCategoryNoticeList("Notice").Take(2))
                    {
                        <tr>
                            <td style="text-align:center;">
                                <span class="label label-success" style="font-size:xx-small">공지 사항</span>
                            </td>
                            <td>
                                @Html.Raw(NoticeHelper.GetReplyImageHTML(notice.ReplyLevel))
                                <a asp-controller="NoticeBoard" asp-action="Details" asp-route-id="@notice.ID">
                                    <strong>
                                        @StringHelper.CutUnicodeString(notice.Title, 30)
                                    </strong>
                                </a>
                                <small>
                                    @Html.Raw(NoticeHelper.GetCommentCountHTML(notice.CommentCount))
                                </small>
                                @Html.Raw(NoticeHelper.GetNewImageHTML(notice.WriteDate))
                            </td>
                            <td style="text-align:center;">
                                @Html.Raw(NoticeHelper.GetFileDownloadLinkHTML(
                                    notice.ID,
                                    notice.FileName,
                                    notice.FileSize.ToString()
                                ))
                            </td>
                            <td style="text-align:center;">
                                @Html.DisplayFor(modelItem => notice.Name)
                            </td>
                            <td style="text-align:center;">
                                @NoticeHelper.GetDateTimeHTML(notice.WriteDate)
                            </td>
                            <td style="text-align:right;">
                                @Html.DisplayFor(modelItem => notice.ReadCount)
                            </td>
                        </tr>
                    }
                </cache>
                @foreach(NoticeModel notice in Model)
                {
                    <tr>
                        <td style="text-align:center;">
                            @if(notice.ReplyLevel == 0)
                            {
                                @Html.DisplayFor(modelItem => notice.ID)
                            }
                            else
                            {
                                <text>&nbsp;</text>
                            }
                        </td>
                        <td>
                            @Html.Raw(NoticeHelper.GetReplyImageHTML(notice.ReplyLevel))
                            <a asp-controller="NoticeBoard" asp-action="Details" asp-route-id="@notice.ID">
                                @StringHelper.CutUnicodeString(notice.Title, 30)
                            </a>
                            <small>
                                @Html.Raw(NoticeHelper.GetCommentCountHTML(notice.CommentCount))
                            </small>
                            @Html.Raw(NoticeHelper.GetNewImageHTML(notice.WriteDate))
                        </td>
                        <td style="text-align:center;">
                            @Html.Raw(NoticeHelper.GetFileDownloadLinkHTML(
                                notice.ID,
                                notice.FileName,
                                notice.FileSize.ToString()
                            ))
                        </td>
                        <td style="text-align:center;">
                            @Html.DisplayFor(modelItem => notice.Name)
                        </td>
                        <td style="text-align:center;">
                            @NoticeHelper.GetDateTimeHTML(notice.WriteDate)
                        </td>
                        <td style="text-align:right;">
                            @Html.DisplayFor(modelItem => notice.ReadCount)
                        </td>
                    </tr>
                }
            </table>
        </td>
    </tr>
    <tr>
        <td style="text-align:center;">
            <paging-helper
                url="@Context.Request.Path"
                page-index='Convert.ToInt32(Context.Request.Query["Page"])'
                page-size="10"
                record-count="@ViewBag.RecordCount"
                search-mode='Convert.ToBoolean(@ViewBag.SearchMode)'
                search-field="@ViewBag.SearchField"
                search-query="@ViewBag.SearchQuery">
            </paging-helper>
        </td>
    </tr>
    <tr>
        <td style="text-align:right;">
            <a asp-action="Create" class="btn btn-primary">글쓰기</a>
        </td>
    </tr>
    <tr>
        <td style="text-align:center;">
            @await Html.PartialAsync("_SearchForm")
            <br />
            @if(ViewBag.SearchMode)
            {
                <a asp-controller="NoticeBoard" asp-action="Index"
                    class="btn btn-success">
                    검색 완료
                </a>
            }
        </td>
    </tr>
</table>
@section Scripts {
    @if(TempData["Message"] != null)
    {
        <link href="~/lib/toastr.js/toastr.css" rel="stylesheet" />
        <script src="~/lib/toastr.js/toastr.min.js"></script>
        <script>
            $(function () {
                toastr.info('@TempData["Message"]');
            });
        </script>
    }
}

 

▶ Views/NoticeBoard/Create.cshtml

@model NoticeModel
<h2 style="text-align:center;">게시판</h2>
@ViewBag.TitleDescription
<hr />
<div class="row">
    <div class="col-md-offset-2 col-md-8 col-md-offset-2">
        <form asp-controller="NoticeBoard" asp-action="Create"
            enctype="multipart/form-data"
            method="post">
            @await Html.PartialAsync("_BoardEditorForm")
        </form>
    </div>
</div>
@section Scripts {
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

 

▶ Views/NoticeBoard/Update.cshtml

@model NoticeModel
<h2 style="text-align:center;">게시판</h2>
@ViewBag.TitleDescription
<hr />
<div class="row">
    <div class="col-md-offset-2 col-md-8 col-md-offset-2">
        <form asp-controller="NoticeBoard" asp-action="Update" method="post"
            enctype="multipart/form-data">
            @await Html.PartialAsync("_BoardEditorForm")
        </form>
    </div>
</div>
@section Scripts {
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

 

▶ Views/NoticeBoard/Reply.cshtml

@model NoticeModel
<h2 style="text-align:center;">게시판</h2>
@ViewBag.TitleDescription
<hr />
<div class="row">
    <div class="col-md-offset-2 col-md-8 col-md-offset-2">
        <form asp-controller="NoticeBoard" asp-action="Reply" method="post"
            enctype="multipart/form-data">
            @await Html.PartialAsync("_BoardEditorForm")
        </form>
    </div>
</div>
@section Scripts {
    @{ await Html.RenderPartialAsync("_ValidationScriptsPartial"); }
}

 

▶ Views/NoticeBoard/Delete.cshtml

@section Scripts {
    <script src="~/lib/bootbox.js/bootbox.js"></script>
}
<script>
    function ConfirmDelete()
    {
        var flag = false;
        var password   = document.getElementById("Password");

        if(password.value.length == 0)
        {
            bootbox.alert("패스워드를 입력해 주시기 바랍니다.", function() { });

            password.focus();

            return false;
        }

        flag = false;

        bootbox.confirm
        (
            "현재 글을 삭제하시겠습니까?",
            function(result)
            {
                if(result == true)
                {
                    document.forms[0].submit();
                }
            }
        );

        return flag;
    }
</script>
<h2 style="text-align:center;">게시판</h2>
<span style="color:#ff0000;">
    게시글 삭제 - 글을 삭제하려면 글 작성시에 기록하였던 패스워드가 필요합니다.
</span>
<hr />
<div style="text-align:center;">
    <form asp-action="Delete" method="post" onsubmit="return ConfirmDelete();">
        <span class="text-danger">@ViewBag.ID</span>번 글을 지우시겠습니까?
        <input type="hidden" name="id" id="id" value="@ViewBag.ID" />
        <br />
        패스워드&nbsp;
        <input type="password" name="password" id="password"
            style="width:120px;display:inline-block;"
            class="form-control" />
        <input type="submit" value="지우기" class="btn btn-danger"
            style="display:inline-block;" />
        <a asp-action="Details" asp-route-id="@ViewBag.ID"
            class="btn btn-default">취소</a>
        <br />
        <span class="text-danger">@ViewBag.Message</span>
        <br />
    </form>
</div>

 

▶ Views/NoticeBoard/DeleteCompleted.cshtml

@{ Layout = null; }
<script>
    window.alert("게시글이 삭제되었습니다.");
    window.location.href = "/NoticeBoard/Index";
</script>

 

▶ Views/NoticeBoard/Details.cshtml

@model NoticeModel
@inject Microsoft.Extensions.Options.IOptions<TestProject.Settings.MainSettings> settings
<h2 style="text-align:center;">게시판</h2>
<span style="color:#ff0000">
    게시글 보기 - 현재 글에 대해서 수정 및 삭제를 할 수 있습니다.
</span>
<hr />
<table style="width:700px;margin-left:auto;margin-right:auto;">
    <tbody>
        <tr style="color:white;background-color:#46698c;">
            <td style="width:80px;text-align:right;height:35px;">
                <b style="font-size:18px">제목</b> :
            </td>
            <td colspan="3">
                <span style="font-weight:bold;font-size:18px;">
                    @Model.Title
                </span>
            </td>
        </tr>
        <tr style="background-color:#efefef;">
            <td class="text-right" style="font-weight:bold;">
                번호&nbsp;
            </td>
            <td>
                <span>@Model.ID</span>
            </td>
            <td class="text-right" style="font-weight:bold;">
                메일 주소&nbsp;
            </td>
            <td>
                <a href="mailto:@Model.MailAddress">@Model.MailAddress</a>
            </td>
        </tr>
        <tr style="background-color:#efefef;">
            <td class="text-right" style="font-weight:bold;">
                성명&nbsp;
            </td>
            <td>
                <span>@Model.Name</span>
            </td>
            <td class="text-right" style="font-weight:bold;">
                홈페이지&nbsp;
            </td>
            <td>
                <span>
                    @Html.Raw(string.Format(
                        "<a href=\"{0}\" target=\"_blank\">{0}</a>",
                        Model.Homepage))
                </span>
            </td>
        </tr>
        <tr style="background-color:#efefef;">
            <td class="text-right" style="font-weight:bold;">
                작성일&nbsp;
            </td>
            <td>
                <span>@Model.WriteDate</span>
            </td>
            <td class="text-right" style="font-weight:bold;">
                IP 주소&nbsp;
            </td>
            <td>
                <span>@Model.WriteIP</span>
            </td>
        </tr>
        <tr style="background-color:#efefef;">
            <td class="text-right" style="font-weight:bold;">
                조회 수&nbsp;
            </td>
            <td>
                <span>@Model.ReadCount</span>
            </td>
            <td class="text-right" style="font-weight:bold;">
                파일&nbsp;
            </td>
            <td>
                @Html.Raw(ViewBag.FileName)
            </td>
        </tr>
        <tr>
            <td colspan="4" style="padding:10px;height:100px;vertical-align:top;">
                &nbsp;
                @Html.Raw(ViewBag.ImageDownloadURL)
                &nbsp;
                @Html.Raw(ViewBag.Content)
            </td>
        </tr>
        <tr>
            <td colspan="4">
                <hr />
            </td>
        </tr>
        <tr>
            <td colspan="4">
                @await Html.PartialAsync(
                    "_BoardCommentControl", (NoticeCommentModel)ViewBag.NoticeComment
                )
            </td>
        </tr>
    </tbody>
</table>
<div style="text-align:center;">
    @if(Model.Category != "Notice" && User.IsInRole("Users") &&
        User.FindFirst("UserID").Value == settings.Value.SiteAdministrator)
    {
        <a asp-action="Pin" asp-route-id="@Model.ID" class="btn btn-danger">
            공지로 올리기
        </a>
    }
    <a asp-action="Delete" asp-route-id="@Model.ID" class="btn btn-default">삭제</a>
    <a asp-action="Update" asp-route-id="@Model.ID" class="btn btn-default">수정</a>
    <a asp-action="Reply"  asp-route-id="@Model.ID" class="btn btn-default">답변</a>
    <a asp-action="Index" class="btn btn-default">목록</a>
</div>
<br />
@section Scripts {
    @if(TempData["Message"] != null)
    {
        <link href="~/lib/toastr.js/toastr.css" rel="stylesheet" />
        <script src="~/lib/toastr.js/toastr.min.js"></script>
        <script>
            $(function () {
                toastr.info('@TempData["Message"]');
            });
        </script>
    }
}

 

▶ Views/NoticeBoard/DeleteComment.cshtml

<h2 style="text-align:center;">게시판</h2>
<span style="color: #ff0000">
    댓글 삭제 - 패스워드를 입력하시면 댓글을 삭제할 수 있습니다.
</span>
<hr />
<form asp-controller="NoticeBoard" asp-action="DeleteComment" method="post">
    <input type="hidden" name="noticeID" value="@ViewBag.NoticeID" />
    <input type="hidden" name="id" value="@ViewBag.ID" />
    <table style="width:500px;margin-left:auto;margin-right:auto;">
        <tr>
            <td class="text-center">
                <i class="glyphicon glyphicon-lock"></i>
                <span style="font-size:12pt;">댓글 삭제</span>
            </td>
        </tr>
        <tr>
            <td class="text-center">
                <span>
                    해당 댓글를 삭제하려면 패스워드를 입력해 주시기 바랍니다.
                </span>
                <br />
                패스워드(<u>P</u>)
                <input type="password" name="password" maxlength="50"
                    class="form-control"
                    style="width:250px;display:inline-block;"
                    tabindex="2"
                    accesskey="P" />
            </td>
        </tr>
        <tr>
            <td style="text-align: center;">
                <input type="submit" value="삭제" class="btn btn-danger" />
                <input type="button" value="뒤로" onclick="history.go(-1);"
                    class="btn btn-default"><br />
                <span class="text-danger">@ViewBag.ErrorMessage</span>
            </td>
        </tr>
    </table>
</form>

 

▶ Startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;

using TestProject.Models;
using TestProject.Settings;

namespace TestProject
{
    /// <summary>
    /// 시작
    /// </summary>
    public class Startup
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Property
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 구성 - Configuration

        /// <summary>
        /// 구성
        /// </summary>
        public IConfiguration Configuration { get; }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Constructor
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 생성자 - Startup(configuration)

        /// <summary>
        /// 생성자
        /// </summary>
        /// <param name="configuration">구성</param>
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        #endregion

        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Public

        #region 서비스 컬렉션 구성하기 - ConfigureServices(services)

        /// <summary>
        /// 서비스 컬렉션 구성하기
        /// </summary>
        /// <param name="services">서비스 컬렉션</param>
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddControllersWithViews();

            services.AddSingleton<IConfiguration>(Configuration);
            services.Configure<MainSettings>(Configuration.GetSection(nameof(MainSettings)));
            services.AddTransient<INoticeRepository, NoticeRepository>();
            services.AddSingleton<ICommentRepository>(new CommentRepository(Configuration["ConnectionStrings:DefaultConnection"]));
        }

        #endregion
        #region 구성하기 - Configure(app, environment)

        /// <summary>
        /// 구성하기
        /// </summary>
        /// <param name="app">애플리케이션 빌더</param>
        /// <param name="environment">웹 호스트 환경</param>
        public void Configure(IApplicationBuilder app, IWebHostEnvironment environment)
        {
            if(environment.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");

                app.UseHsts();
            }

            app.UseHttpsRedirection();

            app.UseStaticFiles();

            app.UseRouting();

            app.UseAuthorization();

            app.UseEndpoints
            (
                endpoints =>
                {
                    endpoints.MapControllerRoute
                    (
                        name    : "default",
                        pattern : "{controller=Home}/{action=Index}/{id?}"
                    );
                }
            );
        }

        #endregion
    }
}

 

▶ Program.cs

using Microsoft.AspNetCore.Hosting;
using Microsoft.Extensions.Hosting;

namespace TestProject
{
    /// <summary>
    /// 프로그램
    /// </summary>
    public class Program
    {
        //////////////////////////////////////////////////////////////////////////////////////////////////// Method
        ////////////////////////////////////////////////////////////////////////////////////////// Static
        //////////////////////////////////////////////////////////////////////////////// Public

        #region 프로그램 시작하기 - Main(argumentArray)

        /// <summary>
        /// 프로그램 시작하기
        /// </summary>
        /// <param name="argumentArray">인자 배열</param>
        public static void Main(string[] argumentArray)
        {
            CreateHostBuilder(argumentArray).Build().Run();
        }

        #endregion
        #region 호스트 빌더 생성하기 - CreateHostBuilder(argumentArray)

        /// <summary>
        /// 호스트 빌더 생성하기
        /// </summary>
        /// <param name="argumentArray">인자 배열</param>
        /// <returns>호스트 빌더</returns>
        public static IHostBuilder CreateHostBuilder(string[] argumentArray) =>
            Host.CreateDefaultBuilder(argumentArray)
                .ConfigureWebHostDefaults
                (
                    builder =>
                    {
                        builder.UseStartup<Startup>();
                    }
                );

        #endregion
    }
}
728x90
반응형
그리드형(광고전용)
Posted by icodebroker

댓글을 달아 주세요