spring:
  datasource:
    url: jdbc:h2:tcp://localhost/~/task_agile_test
    username: sa
    password:
    driver-class-name: org.h2.Driver

  jpa:
    database-platform: org.hibernate.dialect.H2Dialect
    open-in-view: false
    hibernate:
      ddl-auto: create-drop
    properties:
      hibernate:
        show_sql: true
        format_sql: true
        
logging:
  level:
    org.hibernate.type.descriptor.sql: trace
  • spring.datasource : DB 연결정보 
  • spring.jpa.database-platform : 데이터 플래폼을 지정, 참고 
  • spring.jpa.open-in-view : osiv 웹요청이 완료될때까지 영속성을 가짐 (성능상 안 좋음)
  • spring.jpa.hibernate.ddl-auto :
    • none : No action is performed. The schema will not be generated.
    • create : create the schema, the data previously present (if there) in the schema is lost
    • create-drop : (default) create the schema with destroying the data previously present(if there). It also drop the database schema when the SessionFactory is closed.
    • update : update the schema with the given values.
    • validate : validate the schema. It makes no change in the DB.
  • spring.jpa.properties.hibernate.format_sql : 실행쿼리를 가독성있게 표현
  • spring.jpa.properties.hibernate.show-sql : 콘솔에 jpa 실행쿼리
  • logging.level.org.hibernate.type.descriptor.sql : ? 부문을 로그로 출력해줌

Before 어드바이스는 스프링이 제공하는 가장 유용한 어드바이스 중 하나다. Before 어드바이스는 메서드로 넘겨주는 인자를 수정할 수 있으며 메서드를 실행하기 전에 예외를 발생시켜서 실행을 막을 수도 있다.

import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;

import java.lang.reflect.Method;

public class SimpleBeforeAdvice  implements MethodBeforeAdvice {

    public static void main(String[] args) {
        MessageWriter target = new MessageWriter();

        // create the proxy
        ProxyFactory pf = new ProxyFactory();

        pf.addAdvice(new SimpleBeforeAdvice());
        pf.setTarget(target);

        MessageWriter proxy = (MessageWriter) pf.getProxy();

        // write the messages
        proxy.writeMessage();
    }

    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        System.out.println("Before method: " + method.getName());
    }

}

결과

Before method : writeMessage
World


Before 어드바이스를 사용하여 메서드 접근 보안하기

SecureBean클래스 - 보안처리를 할 클래스

package com.lastjava.spring.ch05.security;

public class SecureBean {
 public void writeSecureMessage() {
        System.out.println("Every time I learn something new, "
                + "it pushes some old stuff out of my brain");
    }
}

UserInfo클래스 - 사용자 정보를 저장

package com.lastjava.spring.ch05.security;

public class UserInfo {
 private String username;
    private String password;
   
    public UserInfo(String username, String password) {
        this.username = username;
        this.password = password;
    }
   
    public String getPassword() {
        return password;
    }

    public String getUsername() {
        return username;
    }
}

SecurityManager클래스 - 사용자 인증을 담당하고 그들의 계정 정보를 담아뒸더가 나중에 참조

package com.lastjava.spring.ch05.security;

public class SecurityManager {

    private static ThreadLocal<UserInfo> threadLocal = new ThreadLocal<UserInfo>();

    public void login(String username, String password) {
        // assumes that all credentials
        // are valid for a login
        threadLocal.set(new UserInfo(username, password));
    }

    public void logout() {
        threadLocal.set(null);
    }
   
    public UserInfo getLoggedOnUser() {
        return threadLocal.get();
    }
}

SecurityAdvice클래스 - 해당 메서드 접근전에 실행 메서드실행을 예외처리로 방지한다.

package com.lastjava.spring.ch05.security;

import java.lang.reflect.Method;

import org.springframework.aop.MethodBeforeAdvice;

public class SecurityAdvice implements MethodBeforeAdvice {

    private SecurityManager securityManager;

    public SecurityAdvice() {
        this.securityManager = new SecurityManager();
    }

    public void before(Method method, Object[] args, Object target)
            throws Throwable {
        UserInfo user = securityManager.getLoggedOnUser();

        if (user == null) {
            System.out.println("No user authenticated");
            throw new SecurityException(
                    "You must login before attempting to invoke the method: "
                            + method.getName());
        } else if ("janm".equals(user.getUsername())) {
            System.out.println("Logged in user is janm - OKAY!");
        } else {
            System.out.println("Logged in user is " + user.getUsername()
                    + " NOT GOOD :(");
            throw new SecurityException("User " + user.getUsername()
                    + " is not allowed access to method " + method.getName());
        }

    }
}

SecurityExample 클래스

package com.lastjava.spring.ch05.security;

import org.springframework.aop.framework.ProxyFactory;

public class SecurityExample {
 public static void main(String[] args) {
        // get the security manager
        SecurityManager mgr = new SecurityManager();

        // get the bean
        SecureBean bean = getSecureBean();

        // try as robh
        mgr.login("janm", "*****");
        bean.writeSecureMessage();
        mgr.logout();

        // try as janm
        try {
            mgr.login("mallory", "****");
            bean.writeSecureMessage();
        } catch(SecurityException ex) {
            System.out.println("Exception Caught: " + ex.getMessage());
        } finally {
            mgr.logout();
        }

        // try with no credentials
        try {
            bean.writeSecureMessage();
        } catch(SecurityException ex) {
            System.out.println("Exception Caught: " + ex.getMessage());
        }

    }

     private static SecureBean getSecureBean() {
        // create the target
        SecureBean target = new SecureBean();

        // create the advice
        SecurityAdvice advice = new SecurityAdvice();

        // get the proxy
        ProxyFactory factory = new ProxyFactory();
        factory.setTarget(target);
        factory.addAdvice(advice);

        return (SecureBean)factory.getProxy();

    }
}

결과

Logged in user is janm - OKAY!
Every time I learn something new, it pushes some old stuff out of my brain
Logged in user is mallory NOT GOOD :(
Exception Caught: User mallory is not allowed access to method writeSecureMessage
No user authenticated
Exception Caught: You must login before attempting to invoke the method: writeSecureMessage


 

스프링 AOP 아키텍처
 스프링 AOP의 핵심 아키텍처는 프록시를 기반으로 하고 있다. 어드바이스가 적용된 클래스의 객체를 생성하고 싶다면, 먼저 프록시의 위빙할 모든 액스팩트를 ProxyFactory에 제공하고 반드시 ProxyFactory클래스를 사용하여 해당 클래스 인스턴스의 폭시를 만들어야 한다. 내부적으로 스프링은 두 개의 프록시 구현체가 있다. JDK동적 프록시와 CGLIB프록시다.

스프링 조인포인트
 스프링 AOP에서 주목해야 할 것 하나는 오직 한 종류의 조인포인트인 메서드 호출 조인포인트만 지원한다는 것이다. 이런 단순함이 스프링의 접근성을 높여준다.

스프링 애스팩트
 
스프링 AOP에서 에스팩트는 Advisor 인터페이스를 구현한 클래스의 인스턴스를 말한다. Advisor는 두 개의 하위 인터페이스 IntroductionAdvisor와 PointcutAdvisor가 있다. PointcutAdvisor인터페이스는 포인트컷을 사용하는 모든 어드바이저들이 구현하고 있다. 이것을 사용하여 해당 조인포인트에 어드바이스의 기능을 적용한다.

ProxyFactory 클래스
 
ProxyFactory 클래스는 스프링 AOP에서 프록시 생성과 위빙을 제어한다.
 어드바이스이름  인터페이스  설명
 Before  org.springframework.aop.
MethodBeforeAdivce
 Before 어드바이스를 사용하여, 조인포인트를 실행가히 전에 어떤 처리를 할 수 있다. 스프링에서 조인포인트는 항상 메서드 호출이기 때문에 해당 메서드를 처리하기 전에 전처리 과정을 수행하는 데 사용할 수 있다. Before 어드바이스는 호출하는 메서드에 넘겨주는 인자들을 비롯한 타켓 객체에도 접근할 수 있지만 메서드 실행 자체를 제어할 수는 없다.
 After Returning  org.springframework.aop.
AfterReturningAdvice
 After Returning 어드바이스는 조인포인트에 있는 메서드가 실행을 끝내고 값을 반환 했을 때 실행된다. After Returning 어드바이스는 해당 메서드에 넘겨주는 인자 타켓 객체 그리고 반환값에 접근 할 수 있다. After Returning 어드바이스를 적용하기 전에 이미 메서드는 실행을 마쳤기 때문에 메서드 호출 자체를 제어할 수는 없다.  
 Around  org.aopalliance.intercept.
MethodInterceptor
 스프링에서 Around 어드바이스는 AOP연합 표준
MethodInterceptor를 사용하도록 만들었다. 여러분이 만든 어드바이스는 메서드 실행전과 후에 어떤 작업을 수행할 수 있으며, 어느 시점에 메서드를 실행하지 않을지 제어할 수 있다. 원한다면 해당 메서드 대신에 별도의 로직을 만들어 사용할 수 있다.
 Throws  org.springframework.aop.
ThrowsAdvice
 Throws 어드바이스는 메서드가 호출된 후에 실행되는데 단 해당 메서드가 예외를 던졌을 경우에만 실행한다. 원하면 특정 예외만 잡아내도록 설정할 수 있으며, 해당 예외를 발생시킨 메서드에 넘겨준 인자와 타켓 객체에 접근할 수 있다.
 Introduction  org.springframework.aop.
IntroductionInterceptor
 스프링은 인트로덕션을 특별한 인터셉터 종류로 모델링했다. 인트로덕션 인터셉터를 사용하여, 여러분은 어드바이스를 적용할 메서드 구현체를 지정할 수 있다.

 

AOP 개념

조인포인트(joinpoint) : 애플리케이션 중의 특별한 지점 메서드 호출, 메서드 실행자체, 클래스 초기화, 객체성생

어드바이스(advice) : 조인 포인트에 실행되는 코드

포인트컷(pointcuts) : 조인 포인트의 집합체 어드바이스 실행을 정의

애스팩트(aspect) : 어드바이스와 포인트의 조합

위빙(weaving) : 애스펙트를 주입하는 과정

타켓(target) : 자신의 실행 흐름이 어떠한 aop처리로 인해 수정되는 객체

인트로덕션(introduction) : 객체구조를 수정하여 새로운 메서드나 그 필드를 추가할 수 있는 처리

AOP를 이용한 "Hello, World"

MessageWriter 클래스

package com.lastjava.spring.ch05.simple;

public class MessageWriter {
 public void writeMessage(){
  System.out.print("World");
 }
}

MessageDecorator 클래스

package com.lastjava.spring.ch05.simple;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;

public class MessageDecorator implements MethodInterceptor{
 public Object invoke(MethodInvocation invocation) throws Throwable {
  System.out.print("Hello ");
  Object retVal = invocation.proceed();
  System.out.println("!");
  return retVal;
 }
}

MessageDecorator 클래스

package com.lastjava.spring.ch05.simple;

import org.springframework.aop.framework.ProxyFactory;

public class HelloWorldWeaver {
 public static void main(String[] args){
  
  MessageWriter target = new MessageWriter();
  
  ProxyFactory pf = new ProxyFactory();
  
  pf.addAdvice(new MessageDecorator());
  pf.setTarget(target);
  
  MessageWriter proxy = (MessageWriter) pf.getProxy();
  
  target.writeMessage();
  System.out.println("");
  proxy.writeMessage();
 }
}

결과물

World
Hello World!

출처 : http://cafe.naver.com/headstudy.cafe?iframe_url=/ArticleRead.nhn%3Farticleid=834

com을 등록하는 방법을 찾다가 우연치 않게 발견한 것이다. ㅠㅠ.. 

네이버 카페에서 퍼온글이며 문제가 있을 경우 삭제하겠습니다. 


안녕하세요. 마리입니다. ^^;

 

제가 또 뉴스그룹 서핑 도중... 질문을 하나 만났습니다.

 

"C#으로 COM을 생성하고 asp에서 VB나 C++의 COM처럼 사용할 수 있을까요?"

 

답변은 할 수 있다는 것이였습니다.

 

"C# 으로 만든 컴포넌트는 CCW( COM Callable Wrapper) 에 의해서 regsvr32.exe 로 COM 개체

 

를 등록된 것과 같은 효과로 등록되어질 수 있습니다."

 

저도 궁금하기도 하고 호기심도 생기고 해서... 테스트를 해보려고 맘 먹었습니다.

 

그런데 COM을 첨 해보는지라... 좀 헤맸습니다. ㅋㅋ

 

간단하게 테스트 예제와 함께 과정을 살펴보겠습니다.

 

(아래의 과정은 MS웹 사이트의 기술자료 http://support.microsoft.com/default.aspx?scid=kb;ko;306296 의 방법으로 한 것 입니다.)

 

먼저 새 프로젝트로 C#프로젝트에서 클래스 라이브러리를 하나 만듭니다.

 

이름은 ServicedCOM으로 하겠습니다.

 

자동 생성된 클래스 Class1의 이름을 Com_Test로 변경합니다. 아래... 생성자도 변경 하시는거

 

아시죠? ^^; 소스를 보여드리겠습니다.

 

using System;
using System.EnterpriseServices;

namespace ServicedCOM
{

   public class Com_Test : ServicedComponent
   {
      public Com_Test()
      {   
      } 
      public string DoTrans()
      {   
         return ".NET COM+ Testing";
      }  
   }
}

 

만약에 System.EnterpriseServices네임스페이스가 자동 완성되지 않으신다면...

 

솔루션 탐색기에서 참조추가를 합니다. 거기서 .NET 탭중 쭉..내리시면 있습니다.

 

등록을 해주세요. 소스는 간단한 문자열을 리턴하는 소스입니다.

 

이제 AssemblyInfo.cs파일에서 추가를 합니다.

 

System.EnterpriseServices 역시 상단에 using키워드로 명시하시구요.

 

[assembly: ApplicationActivation(ActivationOption.Library)]
[assembly: ApplicationName("Com_Test")]

 

이제 컴파일을 합니다. 그럼 dll파일이 하나 생기겠죠?

 

이제 어셈블리 이름을 강력하게 지정해야 합니다.

 

  1. 시작 메뉴에서 프로그램을 가리키고 Microsoft Visual Studio .NET을 누른 다음 Visual Studio .NET Tools를 눌러 Visual Studio .NET 명령 프롬프트를 엽니다.
  2. 명령 프롬프트에서 sn.exe -k ServicedCOM.snk를 입력하여 어셈블리에 강력한 이름을 지정합니다.

    강력한 이름으로 어셈블리에 서명하는 방법에 대한 자세한 내용은 .NET Framework SDK 설명서를 참조하십시오.
  3. ServicedCOM.snk를 프로젝트 폴더에 복사합니다.
  4. AssemblyInfo.cs에서 AssemblykeyFile 코드를 다음 코드로 대체합니다.
    [assembly: AssemblyKeyFile("..\\..\\ServicedCOM.snk")]

자... 이제 레지스트리에 등록을 해야 합니다.

 

  1. 시작 메뉴에서 프로그램을 누르고 Microsoft Visual Studio .NET을 누른 다음 Visual Studio .NET Tools를 눌러 .NET 명령 프롬프트를 엽니다.
  2. .NET 명령 프롬프트에서 regsvcs 프로젝트경로\bin\Debug\servicedcom.dll을 입력합니다. 그러면 클래스와 같은 이름의 COM+ 라이브러리 응용 프로그램이 만들어집니다.

이제 완료가 되었습니다.

 

간단한 asp코드로 테스트를 해보세요.

 

<%
set Com = server.createobject("ServicedCOM.Com_Test")
response.write Com.DoTrans()
%>

 

 

 

서핑 하던 도중 다른 방법도 있군요...

 

 

using System;

namespace ServicedCOM
{

   public class Com_Test

   {
      public Com_Test()
      {   
      } 
      public string DoTrans()
      {   
         return ".NET COM+ Testing";
      }  
   }
}

 

어셈블리쪽은 건드리지 마세요. 아... 위의 소스가 아니고 첨의 그대로...

 

regasm 프로젝트경로\bin\Debug\ServicedCOM.dll /codebase

 

이렇게 하면 COM에 등록이 됩니다. 사용법은 위와 동일하구요....

회사에  asp와 웹서비스 연동이 필요해서 만든 메뉴얼..(????)
-------------------------------------------------
Web Service..
좋은 놈이져.. 다들 아시리라 생각하고 설명은 생략하겠습니다.

우리가 사용하고 있는 asp(classic asp)에서는 기본적으로 Web Service와의 연동이 불가능합니다.

그러나 하면된다!!! 의 정신으로 하다보면 asp에서도 웹서비스 연동이 가능합니다.

바로 Soap Toolkit 3.0을 이용하는 방법입니다.

애초에 Soap ToolkitVS용으로 나왔지만, asp에서도 사용가능 합니다.

현재 버전 3.0까지 나왔으며 더 이상의 버전없은 없습니다.

이 후에 대해 관심 있으신 분은 WSE(Web Service Enhancements)를 검색해 보시면 되며 현재 최신 역시 3.0 입니다.

그 이후는 또 WCF(Windows Communication Foundation)로 이어집니다. ~

Soap Toolkit 다운은 아래에서..

http://www.microsoft.com/downloads/details.aspx?FamilyID=c943c0dd-ceec-4088-9753-86f052ec8450&DisplayLang=en

이와 함께 깔아야 되는 것이 MSXML 4.0 입니다.

개발자들은 많이 깔려있지만 일반 서버는 안깔려 있을 수 있습니다. 다운은 역시 아래..

(이것 때문에 삽질 했다는 사례 몇몇 봤습니다.)

http://www.microsoft.com/downloads/details.aspx?displaylang=ko&FamilyID=3144b72b-b4f2-46da-b4b6-c5d7485f2b42

위의 두개를 깔고 나면 soap client가 등록이 됩니다.

ClientPropertyhttp로 읽어들인다고 설정 한 후, MSSoapInit을 이용하여 wsdl을 읽어들입니다.

(로컬에 wsdl을 받은 후 읽는것도 가능합니다.)

그 후 사용하면 됩니다. 간단합니다. =_=

아래는 샘플 소스 입니다.

해석이 필요하시면 도움말을 보세요 soal tookit 깔면 생깁니다.

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

<html xmlns="http://www.w3.org/1999/xhtml" >

<head>

  <meta http-equiv="Content-Type" content="text/html;charset=utf-8" />

  <title>제목 없음</title>

</head>

<body>

  <%

   Dim obj, objXML

   Dim sStr

  

   Set objSoap = Server.CreateObject("MSSOAP.SoapClient30")

   Set objXML = Server.CreateObject("MSXML.DomDocument")

  

   objSoap.ClientProperty("ServerHTTPRequest") = true

   objSoap.MSSoapInit("http://wevservice.address/service.asmx?WSDL")

  

   sStr = objSoap.RequestOrder("<maxmovie></maxmovie>")

  

   Set objSoap = Nothing

   Set objXML = Nothing

  %>

 

  <textarea rows="20" cols="100">

  <%=Response.Write(sStr) %>

  </textarea>

</body>

</html>

계층형 게시판의 경우 최근 답변이 위로 올라오는 형태를 많이 보았다.  하지만 내가 보기에는 아래로 나열 되는 것이 훨씬 더 추적(?) 하기 쉽게 느껴졌다. 최신 답변이 위로 올라올 경우 로직상 편리함이 있다는 사실은 알고 있지만 어자피 유저는 로직따위에는 관심이 없다. 화면에 어떻게 보여지는가 중요하다는 것.. 잡소리는 그만하고 구현 방법에 대해 연구해 보려 한다. 

일단 설명을 자세히 쓴다고 했지만 내용이 이해가지 않는 사람(?) 있을 것이다. (없을 것이라 믿는다... )
그럼 데이터베이스에 그냥 테스트 해보자 그럼 어떤 원리인지 대충 감이 올 것이다
데이터베이스 영역만 회색으로 표시를 해두었다. 무조건 날려보자 하나씩..  

CREATE TABLE BOARD(
BOARD NUMBER NOT NULL,
BGROUP NUMBER NOT NULL,
SORTS NUMBER NOT NULL,
DEPTH NUMBER NOT NULL,
SUBJECT VARCHAR(255)
);

 BOARD BGROUP  SORTS  DEPTH  SUBJECT
         

간단하게 5개가 필요하다  
board : 고유번호
bgroup : 그룹
sorts : 정렬
depth : 깊이
subject : 제목

기본적으로 두개를 추가해본다. 

INSERT INTO BOARD VALUES ('1', '1', '0','0','1번글'); 
INSERT INTO BOARD VALUES ('2', '2', '0','0','2번글');

 board  bgroup  sorts  depth  subject
 1  1  0  0 1번글
 2  2  0  0 2번글

리스트 쿼리를 통해 보도록 하겠다. 

SELECT * FROM BOARD ORDER BY BGROUP DESC, SORTS ASC

 board  bgroup  sorts  depth  subject
 2  2  0  0 2번글
 1  1  0  0 1번글

이제 시작이다. 조회를 할때는 위에 SELECT문으로 조회하자

자 이제 답변에 따라서  2가지로 나눠 진다. 
1. 최신글이 위로 올라가는 형태
2. 최신글이 아래로 내려가는 형태 (1번을 무시하고 봐도 무방!)

1. 답변 최신글이 위로 올라가는 형태

* 공식
1.UPDATE BOARD SET SORTS = SORTS + 1 
  WHERE BGROUP =  (원글의 BGROUP)  AND SORTS >(원글의 SORTS)
2. INSERT INTO BOARD VALUES
   (번호, (원글의 BGROUP), (원글의 SORTS +1), (원글의 DEPTH +1) ,'
 제목')

이렇게 두가지만 해주면 된다. 이해를 위해 따라 해보자
2번글(원글)에 대해서 답변을 달아 보겠다. 
필요한 것은 2번글에 대한 bgroup, sorts, depth에 대한 정보가 필요하다.  비교적 간단하다. 

INSERT INTO BOARD VALUES ('3','2','1','1','2번글 - 1')

bgroup : 2번글과 동일
sorts : 2번글 + 1
depth : 2번글 + 1 을 해준다. 

 BOARD  BGROUP  SORTS  DEPTH  SUBJECT
 2  2  0  0  2번글
 3  2  1  1  2번글 - 1
 1  1  0  0  1번글

하나더 추가해보자 

*INSERT문 하기 전 반드시 해주어야 할 것이 있다. 바로 SORTS는 증가 시켜야 한다는 것이다. 

UPDATE BOARD SET SORTS = SORTS + 1 WHERE BGROUP =  2  AND SORTS > 0

2번글 GROUP과  SORT를 정보를 가지고 UPDATE 시킨다. 

INSERT INTO BOARD VALUES ('4','2','1','1','2번글 -2')

 board  bgroup sorts depth subject 
 2  2  0  0  2번글
 4  2  1  1  2번글-2
 3  2  2  1  2번걸-1
 1  1  0  0  1번글

공식은 나왔다.  깊이도 상관없이 잘 된다. 테스트를 위해 데이터를 더 집어 넣고 확인해보자

UPDATE BOARD SET SORTS = SORTS + 1 WHERE BGROUP =  2  AND SORTS > 1;
INSERT INTO BOARD VALUES ('5','2','2','2','2번글-2-1');

 board  group  sort  depth  subject
 2  2  0  0  2번글
 4  2  1  1  2번글-2
 5  2  2  2  2번글-2-1
 3  2  3  1  2번글-1
 1  1  0  0  1번글

UPDATE BOARD SET SORTS = SORTS + 1 WHERE BGROUP =  2  AND SORTS > 3;
INSERT INTO BOARD VALUES ('6','2','4','2','2번글-1-1');

 board  bgroup  sorts  depth  subject
 2  2  0  0  2번글
 4  2  1  1  2번글-2
 5  2  2  2  2번글-2-1
 3  2  3  1  2번글-1
 6  2  4  2  2번글-1-1
 1  1  0  0  1번글

흠... 간단하군.. 

2. 답변 최신글이 아래로 내려가는 형태
  우선 이야기하자면 약간 복잡하다. 일단 공식을 보자 

*공식
1. SELECT NVL(MIN(SORTS),0) FROM BOARD
   WHERE  BGROUP = (원글의 BGROUP)
   AND SORTS > (원글의 SORTS)
   AND DEPTH <= (원글의 DEPTH)

2-1. 1번이 0 일 경우 

3. SELECT NVL(MAX(SORTS),0) + 1 FROM BOARD 
    WHERE BGROUP = (원글의 BGROUP);

4. INSERT INTO BOARD VALUES 
   (번호, (원글의 BGROUP), (3번값), (원글의 DEPTH +1) ,' 제목')

2-2. 1번이 0이 아닐 경우 

3. UPDATE BOARD SET SORTS = SORTS + 1 
  WHERE BGROUP =  (원글의 BGROUP)  AND SORTS >= (1번값)

4. INSERT INTO BOARD VALUES 
   (번호, (원글의 BGROUP), (1번값), (원글의 DEPTH +1) ,' 제목')

1번에 따라 2단계로 나눠진다. 말하자면 1번에서 SORTS를 구하는 것이다. 
만약 0일 경우 맨 마지막 오는 경우이다.
아닐 경우 중간에 넣는 것인데 중간이후에 있는 다른 것들을 SORTS를 1씩 더해주고 넣는 것이다.

일단 답변이 위로 가는 것을 해보신 분들을 위해서 DELETE문이 있다. 안 해본신 분은 살짝 무시해주자.

DELETE FROM BOARD WHERE SORTS > 0

시작이다. 시작 전에 SELECT문은 이 형태이다. 

SELECT * FROM BOARD ORDER BY BGROUP DESC, SORTS ASC

 board  bgroup  sorts  depth  subject
 2  2  0  0 2번글
 1  1  0  0 1번글

2번글에 대한 답변을 달겠다.

SELECT NVL(MIN(SORTS),0) FROM BOARD
   WHERE  BGROUP = 2
   AND SORTS > 0
   AND DEPTH <= 0

0이 나오는 경우이다. 2-1의 공식으로 진행되어야 한다. 

SELECT NVL(MAX(SORTS),0) + 1 FROM BOARD WHERE BGROUP = 2;

처음 답글이라 수행할 필요없는 구문이지만 우리는 알 수가 없다. 1이 나온다. 이것이 답글이 SORTS가 되는 것이다.

INSERT INTO BOARD VALUES (3, 2, 1, 1 ,'2번글-1')

bgroup : 원글의 bgroup
sorts : 계산한 값
depth : 원글의 depth+1

 board bgroup  sorts  depth  subject 
 2  2  0  0  2번글
 3  2  1  1  2번글-1
 1  1  0  0  1번글

2번글에 대하여 또다시 답글을 달아보자

SELECT NVL(MIN(SORTS),0) FROM BOARD
   WHERE  BGROUP = 2
   AND SORTS > 0
   AND DEPTH <= 0;

SELECT NVL(MAX(SORTS),0) + 1 FROM BOARD WHERE BGROUP = 2;

INSERT INTO BOARD VALUES (4, 2, 2, 1 ,'2번글-2');

물론 0이 나와 2-1 공식에 따르게 된다. 

 board  bgroup sorts  depth  subject 
 2  2  0  2번글
 3  2  1  2번글-1
 4  2  2   2번글-2
 1  1  0  0  1번글

이제 '2번글-1' 에 대한 답변을 달아 보겠다. 

SELECT NVL(MIN(SORTS),0)
FROM BOARD WHERE  BGROUP = 2
AND SORTS > 1
AND DEPTH <= 1;

2가 나올 것이다. 이럴 경우 2-2공식을 이용해야 한다. 빨간색부분을 유의하자 같은 것보다 큰 것이다.

UPDATE BOARD SET SORTS = SORTS + 1 
WHERE BGROUP = 2  AND SORTS >= 2

즉 2번글-2 의 sorts 번호를 올려주고 중간에 밀어 넣는 것이다. 

INSERT INTO BOARD VALUES (5, 2, 2, 2 ,'2번글-1-1')

bgroup : 2번글-1 의 bgroup  
sorts : 계산값
depth : 2번글-1의 depth + 1

 board  bgroup  sorts  depth  board
 2  2  0   0  2번글
 3  2  1  1  2번글-1
 5  2  2  2  2번글-1-1
 4  2  3  1  2번글-2
 1  1  0  0  1번글

board 번호 3번과 4번 사이에 밀어 넣음을 알 수 있다.  

가장 궁금한 것은 공식을 나누는 1번 공식일 것이다. 

SELECT NVL(MIN(SORTS),0) FROM BOARD
   WHERE  BGROUP = (원글의 BGROUP)
   AND SORTS > (원글의 SORTS)
   AND DEPTH <= (원글의 DEPTH)

자세히 보면 원글의 답글의 SORTS 큰 것 중에서 DEPTH 깊은 것을 제하고 가장 작은 SORTS값을 구하는 것이다. 
설명하기가 나의 실력으로는 이해하기 힘들다. 그냥 쓴다.  

더 나은 설명이나 더 좋은 게시판 로직이 있으면 알려주시기 바라면서 이렇게 글을 쓴다. 특히 아래로 떨어지는 것에 대한 게시판 로직이 좀 더 편한 방법이 없는지 궁금하다. 위아래로 떨어지는 방식에 대해서 성능상 이슈나 뭐 그런것에 대하여 태클은.... 어자피 내가 만든 로직도 아니니.. 친철한 설명으로 가르쳐주실 바라며 글을 마친다.  





StringUtils : 문자열로 할 수 있는 수많은 일들. 너무 많다. 일일이 나열하지 않겠다.
HtmlUtils : HTML Escaping 도구

와  html태그를 다 제거해준다. 정말 짱인데...  

php만 있는 줄 알았는데 이런 것도 있구나.. 공부좀 더 해야 겠다

+ Recent posts