<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
	<id>http://wiki.pchero21.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Pchero</id>
	<title>탱이의 잡동사니 - User contributions [en]</title>
	<link rel="self" type="application/atom+xml" href="http://wiki.pchero21.com/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Pchero"/>
	<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php/Special:Contributions/Pchero"/>
	<updated>2026-04-19T18:01:47Z</updated>
	<subtitle>User contributions</subtitle>
	<generator>MediaWiki 1.38.2</generator>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Layouting&amp;diff=4007</id>
		<title>React flow - Layouting</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Layouting&amp;diff=4007"/>
		<updated>2024-03-06T08:04:43Z</updated>

		<summary type="html">&lt;p&gt;Pchero: Created page with &amp;quot;== Overview == React flow 내용정리. 원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/layouting/layouting&amp;lt;/ref&amp;gt;에서 확인할 수 있다.  == Layouting Libraries == We regularly get asked how to handle layouting in React Flow. While we could build some basic layouting into React Flow, we believe that you know your app's requirements best and with so many options out there we think it's better you choose the best right tool for the job (not to mention it'd be whole b...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용정리. 원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/layouting/layouting&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Layouting Libraries ==&lt;br /&gt;
We regularly get asked how to handle layouting in React Flow. While we could build some basic layouting into React Flow, we believe that you know your app's requirements best and with so many options out there we think it's better you choose the best right tool for the job (not to mention it'd be whole bunch of work for us).&lt;br /&gt;
&lt;br /&gt;
To start let's put together a simple example flow that we can use as a base for testing out the different layouting options.&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4006</id>
		<title>React flow - Customizing React Flow</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4006"/>
		<updated>2024-03-06T07:35:09Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Adding an edge label */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서 원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/customization/custom-nodes&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Custom Nodes ==&lt;br /&gt;
A powerful feature of React Flow is the ability to add custom nodes. Within your custom nodes you can render everything you want. You can define multiple source and target handles and render form inputs or charts for example. In this section we will implement a node with an input field that updates some text in another part of the application.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the Custom Node ===&lt;br /&gt;
A custom node is a React component that is wrapped to provide basic functionality like selecting or dragging. From the wrapper component we are passing props like the position or the data besides other props. Let's start to implement the TextUpdaterNode. We are using the Handle component to be able to connect our custom node with other nodes and add an input field to the node:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {useCallback} from 'react';&lt;br /&gt;
import {Handle, Position} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
const handleStype = {left: 10 };&lt;br /&gt;
&lt;br /&gt;
function TextUpdaterNode({data}) {&lt;br /&gt;
  const onChange = useCallback((evt) =&amp;gt; {&lt;br /&gt;
    console.log(evt.target.value);&lt;br /&gt;
  }, []);&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
  &amp;lt;&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;target&amp;quot; position={Position.Top} /&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
      &amp;lt;label htmlFor=&amp;quot;text&amp;quot;&amp;gt;Text:&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;input id=&amp;quot;text&amp;quot; name=&amp;quot;text&amp;quot; onChange={onChange} className=&amp;quot;nodrag&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;source&amp;quot; position={Position.Botton} id=&amp;quot;a&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;Handle&lt;br /&gt;
      type=&amp;quot;source&amp;quot;&lt;br /&gt;
      position={Position.Botton}&lt;br /&gt;
      id=&amp;quot;b&amp;quot;&lt;br /&gt;
      stype={handleStyle}&lt;br /&gt;
    &amp;lt;/Handle&amp;gt;&lt;br /&gt;
  &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
As you see we've added the class name &amp;quot;nodrag&amp;quot; to the input. This prevents dragging within the input field and lets us select text for example.&lt;br /&gt;
&lt;br /&gt;
=== Adding the Node Type ===&lt;br /&gt;
You can add a new node type to React Flow by adding to the nodeTypes props. It's important that the nodeTypes are memoized or defined outside of the component. Otherwise React creates a new object on every render which leads to performance issues and bugs.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodeTypes = useMemo(() =&amp;gt; ({ textUpdater: TextUpdaterNode }), []);&lt;br /&gt;
&lt;br /&gt;
return &amp;lt;ReactFlow nodeTypes={nodeTypes} /&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After defining your new node type, you can use it by using the type node option:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: 'node-1',&lt;br /&gt;
    type: 'textUpdater',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {value: 123},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
After putting all together and adding some basic styles we get a custom node that prints text to the console.&lt;br /&gt;
&lt;br /&gt;
=== Using Multiple Handles ===&lt;br /&gt;
As you can see we added two source handles to the node so that it has two outputs. If you want to connect other nodes with these specific handles, the node id is not enough but you also need to pass the specific handle id. In this case one handle has the id &amp;quot;a&amp;quot; and other one &amp;quot;b&amp;quot;.&lt;br /&gt;
Handle specific edges use the sourceHandle or targetHandle options that reference a handle within a node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const initialEdges = [&lt;br /&gt;
  {id: 'edge-1', source: 'node-1', sourceHandle: 'a', target: 'node-2' },&lt;br /&gt;
  {id: 'edge-2', source: 'node-1', sourceHandle: 'b', target: 'node-3' },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this case the source node is node-1 for both handles but the handle ids are different. One comes from handle id &amp;quot;a&amp;quot; and the other one from &amp;quot;b&amp;quot;. Both edges also have different target nodes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const initialNodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-1&amp;quot;,&lt;br /&gt;
    type: &amp;quot;textUpdater&amp;quot;,&lt;br /&gt;
    position: { x: 0, y: 0 },&lt;br /&gt;
    data: { value: 123 },&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-2&amp;quot;,&lt;br /&gt;
    type: &amp;quot;output&amp;quot;,&lt;br /&gt;
    targetPosition: &amp;quot;top&amp;quot;,&lt;br /&gt;
    position: { x: 0, y: 200 },&lt;br /&gt;
    data: { label: &amp;quot;node 2&amp;quot; },&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-3&amp;quot;,&lt;br /&gt;
    type: &amp;quot;output&amp;quot;,&lt;br /&gt;
    targetPosition: &amp;quot;top&amp;quot;,&lt;br /&gt;
    position: { x: 200, y: 200 },&lt;br /&gt;
    data: { label: &amp;quot;node 3&amp;quot; },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that if you are programmatically changing the position or number of handles in your custom node, you will need to use the useUpdateNodeInternals hook to properly notify ReactFlow of changes. From here you should be abel to build your custom nodes. In most cases we recommend to use custom nodes only. The built-in ones are just basic examples.&lt;br /&gt;
&lt;br /&gt;
=== Custom edges ===&lt;br /&gt;
Like custom nodes, parts of a custom edge in React Flow are just React component: that means you can render anything you want along an edge!&lt;br /&gt;
&lt;br /&gt;
=== A basic custom edge ===&lt;br /&gt;
An edge isn't much use to us if it doesn't render a path between two connected nodes. These paths are always SVG-based and are typically rendered using the &amp;lt;BaseEdge /&amp;gt; component. To calculate the actual SVG path to render, React Flow comes with some handy utility functions:&lt;br /&gt;
* getBezierPath&lt;br /&gt;
* getSimpleBezierPath&lt;br /&gt;
* getSmoothStepPath&lt;br /&gt;
* getStraightPath&lt;br /&gt;
&lt;br /&gt;
To kick start out custom edge, we'll just render a straight path between the source and target.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {BaseEdge, getStraightPath} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
export default function CustomEdge({id, sourceX, sourceY, targetX, targetY}) {&lt;br /&gt;
  const [edgePath] = getStraightPath({&lt;br /&gt;
    sourceX,&lt;br /&gt;
    sourceY,&lt;br /&gt;
    targetX,&lt;br /&gt;
    targetY,&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;&amp;gt;&lt;br /&gt;
      &amp;lt;BaseEdge id={id} path={edgePath} /&amp;gt;&lt;br /&gt;
    &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This gives us a straight edge that behaves the same as the default &amp;quot;straight&amp;quot; edge type. To use it, we also need to update the edgeTypes prop on the &amp;lt;ReactFlow /&amp;gt; component.&lt;br /&gt;
&lt;br /&gt;
It's important to define the edgeTypes object outside of the component or to use React's useMemo hook to prevent unnecessary re-renders. React Flow will show a warning in the console if you forget to do this.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow'&lt;br /&gt;
import CustomEdge from './CustomEdge'&lt;br /&gt;
&lt;br /&gt;
const edgeTypes = {&lt;br /&gt;
  'custom-edge': CustomEdge&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export function Flow() {&lt;br /&gt;
  return &amp;lt;ReactFlow edgeTypes={edgeTypes}... /&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
After defining the edgeTypes objects, we can use our new custom edge by setting the type field of an edge to &amp;quot;custom-edge&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Adding an edge label ===&lt;br /&gt;
One of the more common uses for custom edges is rendering some controls or info along an edge's path. In React Flow we call that an edge label and unlike the edge path, edge labels can be any React component!&lt;br /&gt;
&lt;br /&gt;
To render a custom edge label we must wrap it in the &amp;lt;EdgeLabelRenderer /&amp;gt; component. This is necessary for performance reasons; the edge label renderer is a portal to a single container that all edge labels are rendered into.&lt;br /&gt;
&lt;br /&gt;
Let's add a button to our custom edge that can be used to delete the edge it's attached to:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {&lt;br /&gt;
  BaseEdge,&lt;br /&gt;
  EdgeLabelRenderer,&lt;br /&gt;
  getStraightPath,&lt;br /&gt;
  useReactFlow,&lt;br /&gt;
} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
export default function CustomEdge({id, sourceX, sourceY, targetX, target Y}) {&lt;br /&gt;
  const {setEdge} = useReactFlow();&lt;br /&gt;
  const [edgePath] = getStraightPath({&lt;br /&gt;
    sourceX,&lt;br /&gt;
    sourceY,&lt;br /&gt;
    targetX,&lt;br /&gt;
    targetY,&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;&amp;gt;&lt;br /&gt;
      &amp;lt;BaseEdge id={id} path={edgePath} /&amp;gt;&lt;br /&gt;
      &amp;lt;EdgeLabelRenderer&amp;gt;&lt;br /&gt;
        &amp;lt;button&lt;br /&gt;
          onClick={() =&amp;gt; setEdges((edges) =&amp;gt; edges.filter((e) =&amp;gt; e.id !== id))}&lt;br /&gt;
        &amp;gt;&lt;br /&gt;
          delete&lt;br /&gt;
        &amp;lt;/button&amp;gt;&lt;br /&gt;
      &amp;lt;/EdgeLabelRenderer&amp;gt;&lt;br /&gt;
    &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If we try to use this edge now, we'll see that the button is rendered in the center of the flow (it might be hidden behind &amp;quot;Node A&amp;quot;). Because of the edge label portal, we'll need to do some extra work to position the button ourselves.&lt;br /&gt;
&lt;br /&gt;
Fortunately, the path utils we've already seen can help us with this! Along with the SVG path to render, these functions also return the x and y coordinates of the path's midpoint. We can then use these coordinates to translate our custom edge label's into the right position!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export default function CustomEdge({id, sourceX, sourceYm targetX, targetY}) {&lt;br /&gt;
  const {setEdges} = useReactFlow();&lt;br /&gt;
  const [edgePath, labelX, labelY] = getStraightPath({});&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
    ...&lt;br /&gt;
    &amp;lt;button&lt;br /&gt;
      style={{&lt;br /&gt;
        position: 'absolute',&lt;br /&gt;
        transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,&lt;br /&gt;
        pointerEvents: 'all',&lt;br /&gt;
      }}&lt;br /&gt;
      calssName=&amp;quot;nodrag nopan&amp;quot;&lt;br /&gt;
      onClick={() =&amp;gt; {&lt;br /&gt;
        setEdges((es) =&amp;gt; es.filter((e) =&amp;gt; e.id !== id));&lt;br /&gt;
      }}&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Theming ==&lt;br /&gt;
React Flow has been built with deep customization in mind. Many of our users fully transform the look and feel of React Flow to match their own brand or design system. This guide will introduce you to the different ways you can customize React Flow's appearance.&lt;br /&gt;
&lt;br /&gt;
=== Default styles ===&lt;br /&gt;
React Flow's default styles are enough to get going with the built-in nodes. They provide some sensible defaults for styles like padding, border radius, and animated edges.&lt;br /&gt;
&lt;br /&gt;
You'll typically load these default styles by importing them in you App.jsx file or other entry point&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Without dipping into custom nodes and edges, there are three ways you can style React Flow's basic look:&lt;br /&gt;
* Passing inline styles through style props.&lt;br /&gt;
* Overriding the built-in classes with custom CSS.&lt;br /&gt;
* Overriding the CSS variables React Flow uses.&lt;br /&gt;
&lt;br /&gt;
=== Customizing with style props ===&lt;br /&gt;
The easiest way to start customizing the look and feel of your flows is to use the style prop found on many of React Flow's components to inline your own CSS.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow'&lt;br /&gt;
&lt;br /&gt;
const styles = {&lt;br /&gt;
  background: 'red',&lt;br /&gt;
  width: '100%',&lt;br /&gt;
  height: 300,&lt;br /&gt;
};&lt;br /&gt;
&lt;br /&gt;
export default function Flow() {&lt;br /&gt;
  return &amp;lt;ReactFlow style={styles} nodes={[...]} edges={[...]} /&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4005</id>
		<title>React flow - Customizing React Flow</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4005"/>
		<updated>2024-03-06T07:06:35Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* A basic custom edge */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서 원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/customization/custom-nodes&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Custom Nodes ==&lt;br /&gt;
A powerful feature of React Flow is the ability to add custom nodes. Within your custom nodes you can render everything you want. You can define multiple source and target handles and render form inputs or charts for example. In this section we will implement a node with an input field that updates some text in another part of the application.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the Custom Node ===&lt;br /&gt;
A custom node is a React component that is wrapped to provide basic functionality like selecting or dragging. From the wrapper component we are passing props like the position or the data besides other props. Let's start to implement the TextUpdaterNode. We are using the Handle component to be able to connect our custom node with other nodes and add an input field to the node:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {useCallback} from 'react';&lt;br /&gt;
import {Handle, Position} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
const handleStype = {left: 10 };&lt;br /&gt;
&lt;br /&gt;
function TextUpdaterNode({data}) {&lt;br /&gt;
  const onChange = useCallback((evt) =&amp;gt; {&lt;br /&gt;
    console.log(evt.target.value);&lt;br /&gt;
  }, []);&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
  &amp;lt;&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;target&amp;quot; position={Position.Top} /&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
      &amp;lt;label htmlFor=&amp;quot;text&amp;quot;&amp;gt;Text:&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;input id=&amp;quot;text&amp;quot; name=&amp;quot;text&amp;quot; onChange={onChange} className=&amp;quot;nodrag&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;source&amp;quot; position={Position.Botton} id=&amp;quot;a&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;Handle&lt;br /&gt;
      type=&amp;quot;source&amp;quot;&lt;br /&gt;
      position={Position.Botton}&lt;br /&gt;
      id=&amp;quot;b&amp;quot;&lt;br /&gt;
      stype={handleStyle}&lt;br /&gt;
    &amp;lt;/Handle&amp;gt;&lt;br /&gt;
  &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
As you see we've added the class name &amp;quot;nodrag&amp;quot; to the input. This prevents dragging within the input field and lets us select text for example.&lt;br /&gt;
&lt;br /&gt;
=== Adding the Node Type ===&lt;br /&gt;
You can add a new node type to React Flow by adding to the nodeTypes props. It's important that the nodeTypes are memoized or defined outside of the component. Otherwise React creates a new object on every render which leads to performance issues and bugs.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodeTypes = useMemo(() =&amp;gt; ({ textUpdater: TextUpdaterNode }), []);&lt;br /&gt;
&lt;br /&gt;
return &amp;lt;ReactFlow nodeTypes={nodeTypes} /&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After defining your new node type, you can use it by using the type node option:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: 'node-1',&lt;br /&gt;
    type: 'textUpdater',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {value: 123},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
After putting all together and adding some basic styles we get a custom node that prints text to the console.&lt;br /&gt;
&lt;br /&gt;
=== Using Multiple Handles ===&lt;br /&gt;
As you can see we added two source handles to the node so that it has two outputs. If you want to connect other nodes with these specific handles, the node id is not enough but you also need to pass the specific handle id. In this case one handle has the id &amp;quot;a&amp;quot; and other one &amp;quot;b&amp;quot;.&lt;br /&gt;
Handle specific edges use the sourceHandle or targetHandle options that reference a handle within a node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const initialEdges = [&lt;br /&gt;
  {id: 'edge-1', source: 'node-1', sourceHandle: 'a', target: 'node-2' },&lt;br /&gt;
  {id: 'edge-2', source: 'node-1', sourceHandle: 'b', target: 'node-3' },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this case the source node is node-1 for both handles but the handle ids are different. One comes from handle id &amp;quot;a&amp;quot; and the other one from &amp;quot;b&amp;quot;. Both edges also have different target nodes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const initialNodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-1&amp;quot;,&lt;br /&gt;
    type: &amp;quot;textUpdater&amp;quot;,&lt;br /&gt;
    position: { x: 0, y: 0 },&lt;br /&gt;
    data: { value: 123 },&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-2&amp;quot;,&lt;br /&gt;
    type: &amp;quot;output&amp;quot;,&lt;br /&gt;
    targetPosition: &amp;quot;top&amp;quot;,&lt;br /&gt;
    position: { x: 0, y: 200 },&lt;br /&gt;
    data: { label: &amp;quot;node 2&amp;quot; },&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-3&amp;quot;,&lt;br /&gt;
    type: &amp;quot;output&amp;quot;,&lt;br /&gt;
    targetPosition: &amp;quot;top&amp;quot;,&lt;br /&gt;
    position: { x: 200, y: 200 },&lt;br /&gt;
    data: { label: &amp;quot;node 3&amp;quot; },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that if you are programmatically changing the position or number of handles in your custom node, you will need to use the useUpdateNodeInternals hook to properly notify ReactFlow of changes. From here you should be abel to build your custom nodes. In most cases we recommend to use custom nodes only. The built-in ones are just basic examples.&lt;br /&gt;
&lt;br /&gt;
=== Custom edges ===&lt;br /&gt;
Like custom nodes, parts of a custom edge in React Flow are just React component: that means you can render anything you want along an edge!&lt;br /&gt;
&lt;br /&gt;
=== A basic custom edge ===&lt;br /&gt;
An edge isn't much use to us if it doesn't render a path between two connected nodes. These paths are always SVG-based and are typically rendered using the &amp;lt;BaseEdge /&amp;gt; component. To calculate the actual SVG path to render, React Flow comes with some handy utility functions:&lt;br /&gt;
* getBezierPath&lt;br /&gt;
* getSimpleBezierPath&lt;br /&gt;
* getSmoothStepPath&lt;br /&gt;
* getStraightPath&lt;br /&gt;
&lt;br /&gt;
To kick start out custom edge, we'll just render a straight path between the source and target.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {BaseEdge, getStraightPath} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
export default function CustomEdge({id, sourceX, sourceY, targetX, targetY}) {&lt;br /&gt;
  const [edgePath] = getStraightPath({&lt;br /&gt;
    sourceX,&lt;br /&gt;
    sourceY,&lt;br /&gt;
    targetX,&lt;br /&gt;
    targetY,&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;&amp;gt;&lt;br /&gt;
      &amp;lt;BaseEdge id={id} path={edgePath} /&amp;gt;&lt;br /&gt;
    &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This gives us a straight edge that behaves the same as the default &amp;quot;straight&amp;quot; edge type. To use it, we also need to update the edgeTypes prop on the &amp;lt;ReactFlow /&amp;gt; component.&lt;br /&gt;
&lt;br /&gt;
It's important to define the edgeTypes object outside of the component or to use React's useMemo hook to prevent unnecessary re-renders. React Flow will show a warning in the console if you forget to do this.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow'&lt;br /&gt;
import CustomEdge from './CustomEdge'&lt;br /&gt;
&lt;br /&gt;
const edgeTypes = {&lt;br /&gt;
  'custom-edge': CustomEdge&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export function Flow() {&lt;br /&gt;
  return &amp;lt;ReactFlow edgeTypes={edgeTypes}... /&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
After defining the edgeTypes objects, we can use our new custom edge by setting the type field of an edge to &amp;quot;custom-edge&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
=== Adding an edge label ===&lt;br /&gt;
One of the more common uses for custom edges is rendering some controls or info along an edge's path. In React Flow we call that an edge label and unlike the edge path, edge labels can be any React component!&lt;br /&gt;
&lt;br /&gt;
To render a custom edge label we must wrap it in the &amp;lt;EdgeLabelRenderer /&amp;gt; component. This is necessary for performance reasons; the edge label renderer is a portal to a single container that all edge labels are rendered into.&lt;br /&gt;
&lt;br /&gt;
Let's add a button to our custom edge that can be used to delete the edge it's attached to:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {&lt;br /&gt;
  BaseEdge,&lt;br /&gt;
  EdgeLabelRenderer,&lt;br /&gt;
  getStraightPath,&lt;br /&gt;
  useReactFlow,&lt;br /&gt;
} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
export default function CustomEdge({id, sourceX, sourceY, targetX, target Y}) {&lt;br /&gt;
  const {setEdge} = useReactFlow();&lt;br /&gt;
  const [edgePath] = getStraightPath({&lt;br /&gt;
    sourceX,&lt;br /&gt;
    sourceY,&lt;br /&gt;
    targetX,&lt;br /&gt;
    targetY,&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;&amp;gt;&lt;br /&gt;
      &amp;lt;BaseEdge id={id} path={edgePath} /&amp;gt;&lt;br /&gt;
      &amp;lt;EdgeLabelRenderer&amp;gt;&lt;br /&gt;
        &amp;lt;button&lt;br /&gt;
          onClick={() =&amp;gt; setEdges((edges) =&amp;gt; edges.filter((e) =&amp;gt; e.id !== id))}&lt;br /&gt;
        &amp;gt;&lt;br /&gt;
          delete&lt;br /&gt;
        &amp;lt;/button&amp;gt;&lt;br /&gt;
      &amp;lt;/EdgeLabelRenderer&amp;gt;&lt;br /&gt;
    &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
If we try to use this edge now, we'll see that the button is rendered in the center of the flow (it might be hidden behind &amp;quot;Node A&amp;quot;). Because of the edge label portal, we'll need to do some extra work to position the button ourselves.&lt;br /&gt;
&lt;br /&gt;
Fortunately, the path utils we've already seen can help us with this! Along with the SVG path to render, these functions also return the x and y coordinates of the path's midpoint. We can then use these coordinates to translate our custom edge label's into the right position!&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
export default function CustomEdge({id, sourceX, sourceYm targetX, targetY}) {&lt;br /&gt;
  const {setEdges} = useReactFlow();&lt;br /&gt;
  const [edgePath, labelX, labelY] = getStraightPath({});&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
    ...&lt;br /&gt;
    &amp;lt;button&lt;br /&gt;
      style={{&lt;br /&gt;
        position: 'absolute',&lt;br /&gt;
        transform: `translate(-50%, -50%) translate(${labelX}px, ${labelY}px)`,&lt;br /&gt;
        pointerEvents: 'all',&lt;br /&gt;
      }}&lt;br /&gt;
      calssName=&amp;quot;nodrag nopan&amp;quot;&lt;br /&gt;
      onClick={() =&amp;gt; {&lt;br /&gt;
        setEdges((es) =&amp;gt; es.filter((e) =&amp;gt; e.id !== id));&lt;br /&gt;
      }}&lt;br /&gt;
    &amp;gt;&lt;br /&gt;
    ...&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4004</id>
		<title>React flow - Customizing React Flow</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4004"/>
		<updated>2024-03-06T06:37:16Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* A basic custom edge */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서 원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/customization/custom-nodes&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Custom Nodes ==&lt;br /&gt;
A powerful feature of React Flow is the ability to add custom nodes. Within your custom nodes you can render everything you want. You can define multiple source and target handles and render form inputs or charts for example. In this section we will implement a node with an input field that updates some text in another part of the application.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the Custom Node ===&lt;br /&gt;
A custom node is a React component that is wrapped to provide basic functionality like selecting or dragging. From the wrapper component we are passing props like the position or the data besides other props. Let's start to implement the TextUpdaterNode. We are using the Handle component to be able to connect our custom node with other nodes and add an input field to the node:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {useCallback} from 'react';&lt;br /&gt;
import {Handle, Position} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
const handleStype = {left: 10 };&lt;br /&gt;
&lt;br /&gt;
function TextUpdaterNode({data}) {&lt;br /&gt;
  const onChange = useCallback((evt) =&amp;gt; {&lt;br /&gt;
    console.log(evt.target.value);&lt;br /&gt;
  }, []);&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
  &amp;lt;&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;target&amp;quot; position={Position.Top} /&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
      &amp;lt;label htmlFor=&amp;quot;text&amp;quot;&amp;gt;Text:&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;input id=&amp;quot;text&amp;quot; name=&amp;quot;text&amp;quot; onChange={onChange} className=&amp;quot;nodrag&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;source&amp;quot; position={Position.Botton} id=&amp;quot;a&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;Handle&lt;br /&gt;
      type=&amp;quot;source&amp;quot;&lt;br /&gt;
      position={Position.Botton}&lt;br /&gt;
      id=&amp;quot;b&amp;quot;&lt;br /&gt;
      stype={handleStyle}&lt;br /&gt;
    &amp;lt;/Handle&amp;gt;&lt;br /&gt;
  &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
As you see we've added the class name &amp;quot;nodrag&amp;quot; to the input. This prevents dragging within the input field and lets us select text for example.&lt;br /&gt;
&lt;br /&gt;
=== Adding the Node Type ===&lt;br /&gt;
You can add a new node type to React Flow by adding to the nodeTypes props. It's important that the nodeTypes are memoized or defined outside of the component. Otherwise React creates a new object on every render which leads to performance issues and bugs.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodeTypes = useMemo(() =&amp;gt; ({ textUpdater: TextUpdaterNode }), []);&lt;br /&gt;
&lt;br /&gt;
return &amp;lt;ReactFlow nodeTypes={nodeTypes} /&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After defining your new node type, you can use it by using the type node option:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: 'node-1',&lt;br /&gt;
    type: 'textUpdater',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {value: 123},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
After putting all together and adding some basic styles we get a custom node that prints text to the console.&lt;br /&gt;
&lt;br /&gt;
=== Using Multiple Handles ===&lt;br /&gt;
As you can see we added two source handles to the node so that it has two outputs. If you want to connect other nodes with these specific handles, the node id is not enough but you also need to pass the specific handle id. In this case one handle has the id &amp;quot;a&amp;quot; and other one &amp;quot;b&amp;quot;.&lt;br /&gt;
Handle specific edges use the sourceHandle or targetHandle options that reference a handle within a node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const initialEdges = [&lt;br /&gt;
  {id: 'edge-1', source: 'node-1', sourceHandle: 'a', target: 'node-2' },&lt;br /&gt;
  {id: 'edge-2', source: 'node-1', sourceHandle: 'b', target: 'node-3' },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this case the source node is node-1 for both handles but the handle ids are different. One comes from handle id &amp;quot;a&amp;quot; and the other one from &amp;quot;b&amp;quot;. Both edges also have different target nodes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const initialNodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-1&amp;quot;,&lt;br /&gt;
    type: &amp;quot;textUpdater&amp;quot;,&lt;br /&gt;
    position: { x: 0, y: 0 },&lt;br /&gt;
    data: { value: 123 },&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-2&amp;quot;,&lt;br /&gt;
    type: &amp;quot;output&amp;quot;,&lt;br /&gt;
    targetPosition: &amp;quot;top&amp;quot;,&lt;br /&gt;
    position: { x: 0, y: 200 },&lt;br /&gt;
    data: { label: &amp;quot;node 2&amp;quot; },&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-3&amp;quot;,&lt;br /&gt;
    type: &amp;quot;output&amp;quot;,&lt;br /&gt;
    targetPosition: &amp;quot;top&amp;quot;,&lt;br /&gt;
    position: { x: 200, y: 200 },&lt;br /&gt;
    data: { label: &amp;quot;node 3&amp;quot; },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that if you are programmatically changing the position or number of handles in your custom node, you will need to use the useUpdateNodeInternals hook to properly notify ReactFlow of changes. From here you should be abel to build your custom nodes. In most cases we recommend to use custom nodes only. The built-in ones are just basic examples.&lt;br /&gt;
&lt;br /&gt;
=== Custom edges ===&lt;br /&gt;
Like custom nodes, parts of a custom edge in React Flow are just React component: that means you can render anything you want along an edge!&lt;br /&gt;
&lt;br /&gt;
=== A basic custom edge ===&lt;br /&gt;
An edge isn't much use to us if it doesn't render a path between two connected nodes. These paths are always SVG-based and are typically rendered using the &amp;lt;BaseEdge /&amp;gt; component. To calculate the actual SVG path to render, React Flow comes with some handy utility functions:&lt;br /&gt;
* getBezierPath&lt;br /&gt;
* getSimpleBezierPath&lt;br /&gt;
* getSmoothStepPath&lt;br /&gt;
* getStraightPath&lt;br /&gt;
&lt;br /&gt;
To kick start out custom edge, we'll just render a straight path between the source and target.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {BaseEdge, getStraightPath} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
export default function CustomEdge({id, sourceX, sourceY, targetX, targetY}) {&lt;br /&gt;
  const [edgePath] = getStraightPath({&lt;br /&gt;
    sourceX,&lt;br /&gt;
    sourceY,&lt;br /&gt;
    targetX,&lt;br /&gt;
    targetY,&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;&amp;gt;&lt;br /&gt;
      &amp;lt;BaseEdge id={id} path={edgePath} /&amp;gt;&lt;br /&gt;
    &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This gives us a straight edge that behaves the same as the default &amp;quot;straight&amp;quot; edge type. To use it, we also need to update the edgeTypes prop on the &amp;lt;ReactFlow /&amp;gt; component.&lt;br /&gt;
&lt;br /&gt;
It's important to define the edgeTypes object outside of the component or to use React's useMemo hook to prevent unnecessary re-renders. React Flow will show a warning in the console if you forget to do this.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow'&lt;br /&gt;
import CustomEdge from './CustomEdge'&lt;br /&gt;
&lt;br /&gt;
const edgeTypes = {&lt;br /&gt;
  'custom-edge': CustomEdge&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export function Flow() {&lt;br /&gt;
  return &amp;lt;ReactFlow edgeTypes={edgeTypes}... /&amp;gt;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
After defining the edgeTypes objects, we can use our new custom edge by setting the type field of an edge to &amp;quot;custom-edge&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4003</id>
		<title>React flow - Customizing React Flow</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4003"/>
		<updated>2024-03-06T06:33:44Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Using Multiple Handles */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서 원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/customization/custom-nodes&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Custom Nodes ==&lt;br /&gt;
A powerful feature of React Flow is the ability to add custom nodes. Within your custom nodes you can render everything you want. You can define multiple source and target handles and render form inputs or charts for example. In this section we will implement a node with an input field that updates some text in another part of the application.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the Custom Node ===&lt;br /&gt;
A custom node is a React component that is wrapped to provide basic functionality like selecting or dragging. From the wrapper component we are passing props like the position or the data besides other props. Let's start to implement the TextUpdaterNode. We are using the Handle component to be able to connect our custom node with other nodes and add an input field to the node:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {useCallback} from 'react';&lt;br /&gt;
import {Handle, Position} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
const handleStype = {left: 10 };&lt;br /&gt;
&lt;br /&gt;
function TextUpdaterNode({data}) {&lt;br /&gt;
  const onChange = useCallback((evt) =&amp;gt; {&lt;br /&gt;
    console.log(evt.target.value);&lt;br /&gt;
  }, []);&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
  &amp;lt;&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;target&amp;quot; position={Position.Top} /&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
      &amp;lt;label htmlFor=&amp;quot;text&amp;quot;&amp;gt;Text:&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;input id=&amp;quot;text&amp;quot; name=&amp;quot;text&amp;quot; onChange={onChange} className=&amp;quot;nodrag&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;source&amp;quot; position={Position.Botton} id=&amp;quot;a&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;Handle&lt;br /&gt;
      type=&amp;quot;source&amp;quot;&lt;br /&gt;
      position={Position.Botton}&lt;br /&gt;
      id=&amp;quot;b&amp;quot;&lt;br /&gt;
      stype={handleStyle}&lt;br /&gt;
    &amp;lt;/Handle&amp;gt;&lt;br /&gt;
  &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
As you see we've added the class name &amp;quot;nodrag&amp;quot; to the input. This prevents dragging within the input field and lets us select text for example.&lt;br /&gt;
&lt;br /&gt;
=== Adding the Node Type ===&lt;br /&gt;
You can add a new node type to React Flow by adding to the nodeTypes props. It's important that the nodeTypes are memoized or defined outside of the component. Otherwise React creates a new object on every render which leads to performance issues and bugs.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodeTypes = useMemo(() =&amp;gt; ({ textUpdater: TextUpdaterNode }), []);&lt;br /&gt;
&lt;br /&gt;
return &amp;lt;ReactFlow nodeTypes={nodeTypes} /&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After defining your new node type, you can use it by using the type node option:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: 'node-1',&lt;br /&gt;
    type: 'textUpdater',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {value: 123},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
After putting all together and adding some basic styles we get a custom node that prints text to the console.&lt;br /&gt;
&lt;br /&gt;
=== Using Multiple Handles ===&lt;br /&gt;
As you can see we added two source handles to the node so that it has two outputs. If you want to connect other nodes with these specific handles, the node id is not enough but you also need to pass the specific handle id. In this case one handle has the id &amp;quot;a&amp;quot; and other one &amp;quot;b&amp;quot;.&lt;br /&gt;
Handle specific edges use the sourceHandle or targetHandle options that reference a handle within a node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const initialEdges = [&lt;br /&gt;
  {id: 'edge-1', source: 'node-1', sourceHandle: 'a', target: 'node-2' },&lt;br /&gt;
  {id: 'edge-2', source: 'node-1', sourceHandle: 'b', target: 'node-3' },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this case the source node is node-1 for both handles but the handle ids are different. One comes from handle id &amp;quot;a&amp;quot; and the other one from &amp;quot;b&amp;quot;. Both edges also have different target nodes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const initialNodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-1&amp;quot;,&lt;br /&gt;
    type: &amp;quot;textUpdater&amp;quot;,&lt;br /&gt;
    position: { x: 0, y: 0 },&lt;br /&gt;
    data: { value: 123 },&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-2&amp;quot;,&lt;br /&gt;
    type: &amp;quot;output&amp;quot;,&lt;br /&gt;
    targetPosition: &amp;quot;top&amp;quot;,&lt;br /&gt;
    position: { x: 0, y: 200 },&lt;br /&gt;
    data: { label: &amp;quot;node 2&amp;quot; },&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-3&amp;quot;,&lt;br /&gt;
    type: &amp;quot;output&amp;quot;,&lt;br /&gt;
    targetPosition: &amp;quot;top&amp;quot;,&lt;br /&gt;
    position: { x: 200, y: 200 },&lt;br /&gt;
    data: { label: &amp;quot;node 3&amp;quot; },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that if you are programmatically changing the position or number of handles in your custom node, you will need to use the useUpdateNodeInternals hook to properly notify ReactFlow of changes. From here you should be abel to build your custom nodes. In most cases we recommend to use custom nodes only. The built-in ones are just basic examples.&lt;br /&gt;
&lt;br /&gt;
=== Custom edges ===&lt;br /&gt;
Like custom nodes, parts of a custom edge in React Flow are just React component: that means you can render anything you want along an edge!&lt;br /&gt;
&lt;br /&gt;
=== A basic custom edge ===&lt;br /&gt;
An edge isn't much use to us if it doesn't render a path between two connected nodes. These paths are always SVG-based and are typically rendered using the &amp;lt;BaseEdge /&amp;gt; component. To calculate the actual SVG path to render, React Flow comes with some handy utility functions:&lt;br /&gt;
* getBezierPath&lt;br /&gt;
* getSimpleBezierPath&lt;br /&gt;
* getSmoothStepPath&lt;br /&gt;
* getStraightPath&lt;br /&gt;
&lt;br /&gt;
To kick start out custom edge, we'll just render a straight path between the source and target.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {BaseEdge, getStraightPath} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
export default function CustomEdge({id, sourceX, sourceY, targetX, targetY}) {&lt;br /&gt;
  const [edgePath] = getStraightPath({&lt;br /&gt;
    sourceX,&lt;br /&gt;
    sourceY,&lt;br /&gt;
    targetX,&lt;br /&gt;
    targetY,&lt;br /&gt;
  });&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;&amp;gt;&lt;br /&gt;
      &amp;lt;BaseEdge id={id} path={edgePath} /&amp;gt;&lt;br /&gt;
    &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4002</id>
		<title>React flow - Customizing React Flow</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4002"/>
		<updated>2024-03-06T06:19:51Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Adding the Node Type */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서 원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/customization/custom-nodes&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Custom Nodes ==&lt;br /&gt;
A powerful feature of React Flow is the ability to add custom nodes. Within your custom nodes you can render everything you want. You can define multiple source and target handles and render form inputs or charts for example. In this section we will implement a node with an input field that updates some text in another part of the application.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the Custom Node ===&lt;br /&gt;
A custom node is a React component that is wrapped to provide basic functionality like selecting or dragging. From the wrapper component we are passing props like the position or the data besides other props. Let's start to implement the TextUpdaterNode. We are using the Handle component to be able to connect our custom node with other nodes and add an input field to the node:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {useCallback} from 'react';&lt;br /&gt;
import {Handle, Position} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
const handleStype = {left: 10 };&lt;br /&gt;
&lt;br /&gt;
function TextUpdaterNode({data}) {&lt;br /&gt;
  const onChange = useCallback((evt) =&amp;gt; {&lt;br /&gt;
    console.log(evt.target.value);&lt;br /&gt;
  }, []);&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
  &amp;lt;&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;target&amp;quot; position={Position.Top} /&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
      &amp;lt;label htmlFor=&amp;quot;text&amp;quot;&amp;gt;Text:&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;input id=&amp;quot;text&amp;quot; name=&amp;quot;text&amp;quot; onChange={onChange} className=&amp;quot;nodrag&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;source&amp;quot; position={Position.Botton} id=&amp;quot;a&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;Handle&lt;br /&gt;
      type=&amp;quot;source&amp;quot;&lt;br /&gt;
      position={Position.Botton}&lt;br /&gt;
      id=&amp;quot;b&amp;quot;&lt;br /&gt;
      stype={handleStyle}&lt;br /&gt;
    &amp;lt;/Handle&amp;gt;&lt;br /&gt;
  &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
As you see we've added the class name &amp;quot;nodrag&amp;quot; to the input. This prevents dragging within the input field and lets us select text for example.&lt;br /&gt;
&lt;br /&gt;
=== Adding the Node Type ===&lt;br /&gt;
You can add a new node type to React Flow by adding to the nodeTypes props. It's important that the nodeTypes are memoized or defined outside of the component. Otherwise React creates a new object on every render which leads to performance issues and bugs.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodeTypes = useMemo(() =&amp;gt; ({ textUpdater: TextUpdaterNode }), []);&lt;br /&gt;
&lt;br /&gt;
return &amp;lt;ReactFlow nodeTypes={nodeTypes} /&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After defining your new node type, you can use it by using the type node option:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: 'node-1',&lt;br /&gt;
    type: 'textUpdater',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {value: 123},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
After putting all together and adding some basic styles we get a custom node that prints text to the console.&lt;br /&gt;
&lt;br /&gt;
=== Using Multiple Handles ===&lt;br /&gt;
As you can see we added two source handles to the node so that it has two outputs. If you want to connect other nodes with these specific handles, the node id is not enough but you also need to pass the specific handle id. In this case one handle has the id &amp;quot;a&amp;quot; and other one &amp;quot;b&amp;quot;.&lt;br /&gt;
Handle specific edges use the sourceHandle or targetHandle options that reference a handle within a node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const initialEdges = [&lt;br /&gt;
  {id: 'edge-1', source: 'node-1', sourceHandle: 'a', target: 'node-2' },&lt;br /&gt;
  {id: 'edge-2', source: 'node-1', sourceHandle: 'b', target: 'node-3' },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
In this case the source node is node-1 for both handles but the handle ids are different. One comes from handle id &amp;quot;a&amp;quot; and the other one from &amp;quot;b&amp;quot;. Both edges also have different target nodes:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const initialNodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-1&amp;quot;,&lt;br /&gt;
    type: &amp;quot;textUpdater&amp;quot;,&lt;br /&gt;
    position: { x: 0, y: 0 },&lt;br /&gt;
    data: { value: 123 },&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-2&amp;quot;,&lt;br /&gt;
    type: &amp;quot;output&amp;quot;,&lt;br /&gt;
    targetPosition: &amp;quot;top&amp;quot;,&lt;br /&gt;
    position: { x: 0, y: 200 },&lt;br /&gt;
    data: { label: &amp;quot;node 2&amp;quot; },&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: &amp;quot;node-3&amp;quot;,&lt;br /&gt;
    type: &amp;quot;output&amp;quot;,&lt;br /&gt;
    targetPosition: &amp;quot;top&amp;quot;,&lt;br /&gt;
    position: { x: 200, y: 200 },&lt;br /&gt;
    data: { label: &amp;quot;node 3&amp;quot; },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4001</id>
		<title>React flow - Customizing React Flow</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Customizing_React_Flow&amp;diff=4001"/>
		<updated>2024-03-06T06:07:48Z</updated>

		<summary type="html">&lt;p&gt;Pchero: Created page with &amp;quot;== Overview == Reactflow 정리 문서 원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/customization/custom-nodes&amp;lt;/ref&amp;gt;에서 확인할 수 있다.  == Custom Nodes == A powerful feature of React Flow is the ability to add custom nodes. Within your custom nodes you can render everything you want. You can define multiple source and target handles and render form inputs or charts for example. In this section we will implement a node with an input field that updates some t...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서 원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/customization/custom-nodes&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Custom Nodes ==&lt;br /&gt;
A powerful feature of React Flow is the ability to add custom nodes. Within your custom nodes you can render everything you want. You can define multiple source and target handles and render form inputs or charts for example. In this section we will implement a node with an input field that updates some text in another part of the application.&lt;br /&gt;
&lt;br /&gt;
=== Implementing the Custom Node ===&lt;br /&gt;
A custom node is a React component that is wrapped to provide basic functionality like selecting or dragging. From the wrapper component we are passing props like the position or the data besides other props. Let's start to implement the TextUpdaterNode. We are using the Handle component to be able to connect our custom node with other nodes and add an input field to the node:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {useCallback} from 'react';&lt;br /&gt;
import {Handle, Position} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
const handleStype = {left: 10 };&lt;br /&gt;
&lt;br /&gt;
function TextUpdaterNode({data}) {&lt;br /&gt;
  const onChange = useCallback((evt) =&amp;gt; {&lt;br /&gt;
    console.log(evt.target.value);&lt;br /&gt;
  }, []);&lt;br /&gt;
&lt;br /&gt;
  return(&lt;br /&gt;
  &amp;lt;&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;target&amp;quot; position={Position.Top} /&amp;gt;&lt;br /&gt;
    &amp;lt;div&amp;gt;&lt;br /&gt;
      &amp;lt;label htmlFor=&amp;quot;text&amp;quot;&amp;gt;Text:&amp;lt;/label&amp;gt;&lt;br /&gt;
      &amp;lt;input id=&amp;quot;text&amp;quot; name=&amp;quot;text&amp;quot; onChange={onChange} className=&amp;quot;nodrag&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
    &amp;lt;Handle type=&amp;quot;source&amp;quot; position={Position.Botton} id=&amp;quot;a&amp;quot; /&amp;gt;&lt;br /&gt;
    &amp;lt;Handle&lt;br /&gt;
      type=&amp;quot;source&amp;quot;&lt;br /&gt;
      position={Position.Botton}&lt;br /&gt;
      id=&amp;quot;b&amp;quot;&lt;br /&gt;
      stype={handleStyle}&lt;br /&gt;
    &amp;lt;/Handle&amp;gt;&lt;br /&gt;
  &amp;lt;/&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
As you see we've added the class name &amp;quot;nodrag&amp;quot; to the input. This prevents dragging within the input field and lets us select text for example.&lt;br /&gt;
&lt;br /&gt;
=== Adding the Node Type ===&lt;br /&gt;
You can add a new node type to React Flow by adding to the nodeTypes props. It's important that the nodeTypes are memoized or defined outside of the component. Otherwise React creates a new object on every render which leads to performance issues and bugs.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodeTypes = useMemo(() =&amp;gt; ({ textUpdater: TextUpdaterNode }), []);&lt;br /&gt;
&lt;br /&gt;
return &amp;lt;ReactFlow nodeTypes={nodeTypes} /&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After defining your new node type, you can use it by using the type node option:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: 'node-1',&lt;br /&gt;
    type: 'textUpdater',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {value: 123},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
After putting all together and adding some basic styles we get a custom node that prints text to the console.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=4000</id>
		<title>React flow- Getting Started</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=4000"/>
		<updated>2024-03-06T04:14:54Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Handle Connections */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/getting-started/installation-and-requirements&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Installation and Requirements ===&lt;br /&gt;
The React Flow package is published under reactflow on npm and installable via:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ npm install reactflow&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can import the React Flow component and the styles in your application:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Prior Experience Needed ===&lt;br /&gt;
React Flow is a React library. That means React developers will feel comfortable using it. If basic React terms and concepts like states, props, components, and hooks are unfamiliar to you, you might need to learn more about React before being able to use React Flow fully.&lt;br /&gt;
&lt;br /&gt;
== Building a Flow ==&lt;br /&gt;
=== Getting Started ===&lt;br /&gt;
Let's create an empty flow with a controls panel and a background. For this we need to import the components from the reactflow package:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Background, Controls } from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can now use the components to render an empty flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Controls, Background } from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return (&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are three important things to keep in mind here:&lt;br /&gt;
* You need to import the styles. Otherwise, React Flow won't work.&lt;br /&gt;
* The parent container needs a width and height, because React Flow uses its parent dimensions.&lt;br /&gt;
* If you have multiple flows on one page, you need to pass a unique id prop to each component to make React Flow work properly.&lt;br /&gt;
&lt;br /&gt;
=== Adding Nodes ===&lt;br /&gt;
Now that the flow is set up, let's add some nodes. To do this, you need to create an array with node objects like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',  // required&lt;br /&gt;
    position: { x: 0, y: 0}, // required&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These nodes can now be added to the flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, {Controls, Background} from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow nodex={nodes}&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's add another node, configure labels and use the node type input for the first node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {label: 'Hello'},&lt;br /&gt;
    type: 'input',&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: '2',&lt;br /&gt;
    position: {x: 100, y: 100},&lt;br /&gt;
    data: { label: 'World' },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Adding an Edge ===&lt;br /&gt;
Now that we have two nodes, let's connect them with an edge.&lt;br /&gt;
&lt;br /&gt;
To make an edge, we need to specify two attributes: the source node(where the edge begins) and the target node(where the edge ends). We use the id of the two nodes to specify this (in our example, our two nodes have ids of &amp;quot;1&amp;quot; and &amp;quot;2&amp;quot;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [{id: '1-2&amp;quot;, source: '1', target: '2'}]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's give this edge two properties that are built into React Flow, a label and a different type.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1-2',&lt;br /&gt;
    source: '1',&lt;br /&gt;
    target: '2',&lt;br /&gt;
    label: 'to the',&lt;br /&gt;
    type: 'step',&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Interactivity ==&lt;br /&gt;
Let's make it so we can select, drag, and remove nodes and edges.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
In this Getting Started tutorial, we are going to use a &amp;quot;controlled component&amp;quot;, which is typically the best and most flexible way to use React Flow in your own applications. You can also use React Flow in an uncontrolled way.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handle Change Events ===&lt;br /&gt;
First let's import a few things. To manage the changes in React, we'll be using useState and the two helper function applyEdgeChanges and applyNodeChanges from React Flow.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {useState, useCallback } from 'react';&lt;br /&gt;
import ReactFlow, {applyEdgeChanges, applyNodeChanges} from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We're going to set up states for both the nodes and edges:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const [nodes, setNodes] = useState(initialNodes);&lt;br /&gt;
const [edges, setEdges] = useState(initialEdges);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Directly beneath that, we'll add these two functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onNodesChange = useCallback(&lt;br /&gt;
  (changes) =&amp;gt; setNode((nds) =&amp;gt; applyNodeChanges(changes, ndx)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
const onEdgesChange = useCallback(&lt;br /&gt;
  (changes) =&amp;gt; setEdges((eds) =&amp;gt; applyEdgeChanges(changes, eds)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you drag or select a node, the onNodeChange handler gets called. With help of the applyNodeChanges function you can then apply those changes to your current node state.&lt;br /&gt;
&lt;br /&gt;
=== Handle Connections ===&lt;br /&gt;
One last piece is missing: connecting nodes manually. For this we need to implement an onConnect handler and pass it to the &amp;lt;ReactFlow /&amp;gt; component as well.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {addEdge} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
const [edges, setEdges] = useState(initialEdges);&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (params) =&amp;gt; setEdges((eds) =&amp;gt; addEdge(params, eds)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3999</id>
		<title>React flow- Getting Started</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3999"/>
		<updated>2024-03-06T04:14:14Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Handle Connections */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/getting-started/installation-and-requirements&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Installation and Requirements ===&lt;br /&gt;
The React Flow package is published under reactflow on npm and installable via:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ npm install reactflow&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can import the React Flow component and the styles in your application:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Prior Experience Needed ===&lt;br /&gt;
React Flow is a React library. That means React developers will feel comfortable using it. If basic React terms and concepts like states, props, components, and hooks are unfamiliar to you, you might need to learn more about React before being able to use React Flow fully.&lt;br /&gt;
&lt;br /&gt;
== Building a Flow ==&lt;br /&gt;
=== Getting Started ===&lt;br /&gt;
Let's create an empty flow with a controls panel and a background. For this we need to import the components from the reactflow package:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Background, Controls } from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can now use the components to render an empty flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Controls, Background } from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return (&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are three important things to keep in mind here:&lt;br /&gt;
* You need to import the styles. Otherwise, React Flow won't work.&lt;br /&gt;
* The parent container needs a width and height, because React Flow uses its parent dimensions.&lt;br /&gt;
* If you have multiple flows on one page, you need to pass a unique id prop to each component to make React Flow work properly.&lt;br /&gt;
&lt;br /&gt;
=== Adding Nodes ===&lt;br /&gt;
Now that the flow is set up, let's add some nodes. To do this, you need to create an array with node objects like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',  // required&lt;br /&gt;
    position: { x: 0, y: 0}, // required&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These nodes can now be added to the flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, {Controls, Background} from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow nodex={nodes}&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's add another node, configure labels and use the node type input for the first node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {label: 'Hello'},&lt;br /&gt;
    type: 'input',&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: '2',&lt;br /&gt;
    position: {x: 100, y: 100},&lt;br /&gt;
    data: { label: 'World' },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Adding an Edge ===&lt;br /&gt;
Now that we have two nodes, let's connect them with an edge.&lt;br /&gt;
&lt;br /&gt;
To make an edge, we need to specify two attributes: the source node(where the edge begins) and the target node(where the edge ends). We use the id of the two nodes to specify this (in our example, our two nodes have ids of &amp;quot;1&amp;quot; and &amp;quot;2&amp;quot;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [{id: '1-2&amp;quot;, source: '1', target: '2'}]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's give this edge two properties that are built into React Flow, a label and a different type.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1-2',&lt;br /&gt;
    source: '1',&lt;br /&gt;
    target: '2',&lt;br /&gt;
    label: 'to the',&lt;br /&gt;
    type: 'step',&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Interactivity ==&lt;br /&gt;
Let's make it so we can select, drag, and remove nodes and edges.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
In this Getting Started tutorial, we are going to use a &amp;quot;controlled component&amp;quot;, which is typically the best and most flexible way to use React Flow in your own applications. You can also use React Flow in an uncontrolled way.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handle Change Events ===&lt;br /&gt;
First let's import a few things. To manage the changes in React, we'll be using useState and the two helper function applyEdgeChanges and applyNodeChanges from React Flow.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {useState, useCallback } from 'react';&lt;br /&gt;
import ReactFlow, {applyEdgeChanges, applyNodeChanges} from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We're going to set up states for both the nodes and edges:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const [nodes, setNodes] = useState(initialNodes);&lt;br /&gt;
const [edges, setEdges] = useState(initialEdges);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Directly beneath that, we'll add these two functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onNodesChange = useCallback(&lt;br /&gt;
  (changes) =&amp;gt; setNode((nds) =&amp;gt; applyNodeChanges(changes, ndx)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
const onEdgesChange = useCallback(&lt;br /&gt;
  (changes) =&amp;gt; setEdges((eds) =&amp;gt; applyEdgeChanges(changes, eds)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you drag or select a node, the onNodeChange handler gets called. With help of the applyNodeChanges function you can then apply those changes to your current node state.&lt;br /&gt;
&lt;br /&gt;
=== Handle Connections ===&lt;br /&gt;
One last piece is missing: connecting nodes manually. For this we need to implement an onConnect handler and pass it to the &amp;lt;ReactFlow /&amp;gt; component as well.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {addEdge} from 'reactflow';&lt;br /&gt;
&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (params) =&amp;gt; setEdges((eds) =&amp;gt; addEdge(params, eds)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3998</id>
		<title>React flow- Getting Started</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3998"/>
		<updated>2024-03-06T04:03:30Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Adding Interactivity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/getting-started/installation-and-requirements&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Installation and Requirements ===&lt;br /&gt;
The React Flow package is published under reactflow on npm and installable via:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ npm install reactflow&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can import the React Flow component and the styles in your application:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Prior Experience Needed ===&lt;br /&gt;
React Flow is a React library. That means React developers will feel comfortable using it. If basic React terms and concepts like states, props, components, and hooks are unfamiliar to you, you might need to learn more about React before being able to use React Flow fully.&lt;br /&gt;
&lt;br /&gt;
== Building a Flow ==&lt;br /&gt;
=== Getting Started ===&lt;br /&gt;
Let's create an empty flow with a controls panel and a background. For this we need to import the components from the reactflow package:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Background, Controls } from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can now use the components to render an empty flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Controls, Background } from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return (&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are three important things to keep in mind here:&lt;br /&gt;
* You need to import the styles. Otherwise, React Flow won't work.&lt;br /&gt;
* The parent container needs a width and height, because React Flow uses its parent dimensions.&lt;br /&gt;
* If you have multiple flows on one page, you need to pass a unique id prop to each component to make React Flow work properly.&lt;br /&gt;
&lt;br /&gt;
=== Adding Nodes ===&lt;br /&gt;
Now that the flow is set up, let's add some nodes. To do this, you need to create an array with node objects like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',  // required&lt;br /&gt;
    position: { x: 0, y: 0}, // required&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These nodes can now be added to the flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, {Controls, Background} from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow nodex={nodes}&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's add another node, configure labels and use the node type input for the first node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {label: 'Hello'},&lt;br /&gt;
    type: 'input',&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: '2',&lt;br /&gt;
    position: {x: 100, y: 100},&lt;br /&gt;
    data: { label: 'World' },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Adding an Edge ===&lt;br /&gt;
Now that we have two nodes, let's connect them with an edge.&lt;br /&gt;
&lt;br /&gt;
To make an edge, we need to specify two attributes: the source node(where the edge begins) and the target node(where the edge ends). We use the id of the two nodes to specify this (in our example, our two nodes have ids of &amp;quot;1&amp;quot; and &amp;quot;2&amp;quot;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [{id: '1-2&amp;quot;, source: '1', target: '2'}]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's give this edge two properties that are built into React Flow, a label and a different type.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1-2',&lt;br /&gt;
    source: '1',&lt;br /&gt;
    target: '2',&lt;br /&gt;
    label: 'to the',&lt;br /&gt;
    type: 'step',&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Interactivity ==&lt;br /&gt;
Let's make it so we can select, drag, and remove nodes and edges.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
In this Getting Started tutorial, we are going to use a &amp;quot;controlled component&amp;quot;, which is typically the best and most flexible way to use React Flow in your own applications. You can also use React Flow in an uncontrolled way.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handle Change Events ===&lt;br /&gt;
First let's import a few things. To manage the changes in React, we'll be using useState and the two helper function applyEdgeChanges and applyNodeChanges from React Flow.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {useState, useCallback } from 'react';&lt;br /&gt;
import ReactFlow, {applyEdgeChanges, applyNodeChanges} from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We're going to set up states for both the nodes and edges:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const [nodes, setNodes] = useState(initialNodes);&lt;br /&gt;
const [edges, setEdges] = useState(initialEdges);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Directly beneath that, we'll add these two functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onNodesChange = useCallback(&lt;br /&gt;
  (changes) =&amp;gt; setNode((nds) =&amp;gt; applyNodeChanges(changes, ndx)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
const onEdgesChange = useCallback(&lt;br /&gt;
  (changes) =&amp;gt; setEdges((eds) =&amp;gt; applyEdgeChanges(changes, eds)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you drag or select a node, the onNodeChange handler gets called. With help of the applyNodeChanges function you can then apply those changes to your current node state.&lt;br /&gt;
&lt;br /&gt;
=== Handle Connections ===&lt;br /&gt;
One last piece is missing: connecting nodes manually. For this we need to implement an onConnect handler and pass it to the &amp;lt;ReactFlow /&amp;gt; component as well.&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3997</id>
		<title>React flow- Getting Started</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3997"/>
		<updated>2024-03-05T12:21:56Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Adding Interactivity */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/getting-started/installation-and-requirements&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Installation and Requirements ===&lt;br /&gt;
The React Flow package is published under reactflow on npm and installable via:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ npm install reactflow&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can import the React Flow component and the styles in your application:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Prior Experience Needed ===&lt;br /&gt;
React Flow is a React library. That means React developers will feel comfortable using it. If basic React terms and concepts like states, props, components, and hooks are unfamiliar to you, you might need to learn more about React before being able to use React Flow fully.&lt;br /&gt;
&lt;br /&gt;
== Building a Flow ==&lt;br /&gt;
=== Getting Started ===&lt;br /&gt;
Let's create an empty flow with a controls panel and a background. For this we need to import the components from the reactflow package:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Background, Controls } from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can now use the components to render an empty flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Controls, Background } from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return (&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are three important things to keep in mind here:&lt;br /&gt;
* You need to import the styles. Otherwise, React Flow won't work.&lt;br /&gt;
* The parent container needs a width and height, because React Flow uses its parent dimensions.&lt;br /&gt;
* If you have multiple flows on one page, you need to pass a unique id prop to each component to make React Flow work properly.&lt;br /&gt;
&lt;br /&gt;
=== Adding Nodes ===&lt;br /&gt;
Now that the flow is set up, let's add some nodes. To do this, you need to create an array with node objects like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',  // required&lt;br /&gt;
    position: { x: 0, y: 0}, // required&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These nodes can now be added to the flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, {Controls, Background} from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow nodex={nodes}&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's add another node, configure labels and use the node type input for the first node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {label: 'Hello'},&lt;br /&gt;
    type: 'input',&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: '2',&lt;br /&gt;
    position: {x: 100, y: 100},&lt;br /&gt;
    data: { label: 'World' },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Adding an Edge ===&lt;br /&gt;
Now that we have two nodes, let's connect them with an edge.&lt;br /&gt;
&lt;br /&gt;
To make an edge, we need to specify two attributes: the source node(where the edge begins) and the target node(where the edge ends). We use the id of the two nodes to specify this (in our example, our two nodes have ids of &amp;quot;1&amp;quot; and &amp;quot;2&amp;quot;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [{id: '1-2&amp;quot;, source: '1', target: '2'}]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's give this edge two properties that are built into React Flow, a label and a different type.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1-2',&lt;br /&gt;
    source: '1',&lt;br /&gt;
    target: '2',&lt;br /&gt;
    label: 'to the',&lt;br /&gt;
    type: 'step',&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Interactivity ==&lt;br /&gt;
Let's make it so we can select, drag, and remove nodes and edges.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
In this Getting Started tutorial, we are going to use a &amp;quot;controlled component&amp;quot;, which is typically the best and most flexible way to use React Flow in your own applications. You can also use React Flow in an uncontrolled way.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handle Change Events ===&lt;br /&gt;
First let's import a few things. To manage the changes in React, we'll be using useState and the two helper function applyEdgeChanges and applyNodeChanges from React Flow.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {useState, useCallback } from 'react';&lt;br /&gt;
import ReactFlow, {applyEdgeChanges, applyNodeChanges} from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We're going to set up states for both the nodes and edges:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const [nodes, setNodes] = useState(initialNodes);&lt;br /&gt;
const [edges, setEdges] = useState(initialEdges);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Directly beneath that, we'll add these two functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onNodesChange = useCallback(&lt;br /&gt;
  (changes) =&amp;gt; setNode((nds) =&amp;gt; applyNodeChanges(changes, ndx)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
const onEdgesChange = useCallback(&lt;br /&gt;
  (changes) =&amp;gt; setEdges((eds) =&amp;gt; applyEdgeChanges(changes, eds)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you drag or select a node, the onNodeChange handler gets called. With help of the applyNodeChanges function you can then apply those changes to your current node state.&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3996</id>
		<title>React flow- Getting Started</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3996"/>
		<updated>2024-03-05T07:12:27Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Adding an Edge */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/getting-started/installation-and-requirements&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Installation and Requirements ===&lt;br /&gt;
The React Flow package is published under reactflow on npm and installable via:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ npm install reactflow&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can import the React Flow component and the styles in your application:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Prior Experience Needed ===&lt;br /&gt;
React Flow is a React library. That means React developers will feel comfortable using it. If basic React terms and concepts like states, props, components, and hooks are unfamiliar to you, you might need to learn more about React before being able to use React Flow fully.&lt;br /&gt;
&lt;br /&gt;
== Building a Flow ==&lt;br /&gt;
=== Getting Started ===&lt;br /&gt;
Let's create an empty flow with a controls panel and a background. For this we need to import the components from the reactflow package:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Background, Controls } from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can now use the components to render an empty flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Controls, Background } from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return (&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are three important things to keep in mind here:&lt;br /&gt;
* You need to import the styles. Otherwise, React Flow won't work.&lt;br /&gt;
* The parent container needs a width and height, because React Flow uses its parent dimensions.&lt;br /&gt;
* If you have multiple flows on one page, you need to pass a unique id prop to each component to make React Flow work properly.&lt;br /&gt;
&lt;br /&gt;
=== Adding Nodes ===&lt;br /&gt;
Now that the flow is set up, let's add some nodes. To do this, you need to create an array with node objects like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',  // required&lt;br /&gt;
    position: { x: 0, y: 0}, // required&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These nodes can now be added to the flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, {Controls, Background} from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow nodex={nodes}&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's add another node, configure labels and use the node type input for the first node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {label: 'Hello'},&lt;br /&gt;
    type: 'input',&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: '2',&lt;br /&gt;
    position: {x: 100, y: 100},&lt;br /&gt;
    data: { label: 'World' },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Adding an Edge ===&lt;br /&gt;
Now that we have two nodes, let's connect them with an edge.&lt;br /&gt;
&lt;br /&gt;
To make an edge, we need to specify two attributes: the source node(where the edge begins) and the target node(where the edge ends). We use the id of the two nodes to specify this (in our example, our two nodes have ids of &amp;quot;1&amp;quot; and &amp;quot;2&amp;quot;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [{id: '1-2&amp;quot;, source: '1', target: '2'}]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's give this edge two properties that are built into React Flow, a label and a different type.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1-2',&lt;br /&gt;
    source: '1',&lt;br /&gt;
    target: '2',&lt;br /&gt;
    label: 'to the',&lt;br /&gt;
    type: 'step',&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Adding Interactivity ==&lt;br /&gt;
Let's make it so we can select, drag, and remove nodes and edges.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
In this Getting Started tutorial, we are going to use a &amp;quot;controlled component&amp;quot;, which is typically the best and most flexible way to use React Flow in your own applications. You can also use React Flow in an uncontrolled way.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Handle Change Events ===&lt;br /&gt;
First let's import a few things. To manage the changes in React, we'll be using useState and the two helper function applyEdgeChanges and applyNodeChanges from React Flow.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import {useState, useCallback } from 'react';&lt;br /&gt;
import ReactFlow, {applyEdgeChanges, applyNodeChanges} from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We're going to set up states for both the nodes and edges:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const [nodes, setNodes] = useState(initialNodes);&lt;br /&gt;
const [edges, setEdges] = useState(initialEdges);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Directly beneath that, we'll add these two functions:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onNodesChange = useCallback(&lt;br /&gt;
  (changes) =&amp;gt; setNode((nds) =&amp;gt; applyNodeChanges(changes, ndx)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
const onEdgesChange = useCallback(&lt;br /&gt;
  (changes) =&amp;gt; setEdges((eds) =&amp;gt; applyEdgeChanges(changes, eds)),&lt;br /&gt;
  [],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
When you drag or select a node, the onNodeChange handler gets called. With help of the applyNodeChanges function you can then apply&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3995</id>
		<title>React flow- Getting Started</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3995"/>
		<updated>2024-03-05T06:57:47Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Adding Nodes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/getting-started/installation-and-requirements&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Installation and Requirements ===&lt;br /&gt;
The React Flow package is published under reactflow on npm and installable via:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ npm install reactflow&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can import the React Flow component and the styles in your application:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Prior Experience Needed ===&lt;br /&gt;
React Flow is a React library. That means React developers will feel comfortable using it. If basic React terms and concepts like states, props, components, and hooks are unfamiliar to you, you might need to learn more about React before being able to use React Flow fully.&lt;br /&gt;
&lt;br /&gt;
== Building a Flow ==&lt;br /&gt;
=== Getting Started ===&lt;br /&gt;
Let's create an empty flow with a controls panel and a background. For this we need to import the components from the reactflow package:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Background, Controls } from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can now use the components to render an empty flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Controls, Background } from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return (&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are three important things to keep in mind here:&lt;br /&gt;
* You need to import the styles. Otherwise, React Flow won't work.&lt;br /&gt;
* The parent container needs a width and height, because React Flow uses its parent dimensions.&lt;br /&gt;
* If you have multiple flows on one page, you need to pass a unique id prop to each component to make React Flow work properly.&lt;br /&gt;
&lt;br /&gt;
=== Adding Nodes ===&lt;br /&gt;
Now that the flow is set up, let's add some nodes. To do this, you need to create an array with node objects like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',  // required&lt;br /&gt;
    position: { x: 0, y: 0}, // required&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These nodes can now be added to the flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, {Controls, Background} from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow nodex={nodes}&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's add another node, configure labels and use the node type input for the first node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {label: 'Hello'},&lt;br /&gt;
    type: 'input',&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: '2',&lt;br /&gt;
    position: {x: 100, y: 100},&lt;br /&gt;
    data: { label: 'World' },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Adding an Edge ===&lt;br /&gt;
Now that we have two nodes, let's connect them with an edge.&lt;br /&gt;
&lt;br /&gt;
To make an edge, we need to specify two attributes: the source node(where the edge begins) and the target node(where the edge ends). We use the id of the two nodes to specify this (in our example, our two nodes have ids of &amp;quot;1&amp;quot; and &amp;quot;2&amp;quot;):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [{id: '1-2&amp;quot;, source: '1', target: '2'}]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's give this edge two properties that are built into React Flow, a label and a different type.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const edges = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1-2',&lt;br /&gt;
    source: '1',&lt;br /&gt;
    target: '2',&lt;br /&gt;
    label: 'to the',&lt;br /&gt;
    type: 'step',&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3994</id>
		<title>React flow- Getting Started</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3994"/>
		<updated>2024-03-05T06:47:32Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Adding Nodes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/getting-started/installation-and-requirements&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Installation and Requirements ===&lt;br /&gt;
The React Flow package is published under reactflow on npm and installable via:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ npm install reactflow&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can import the React Flow component and the styles in your application:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Prior Experience Needed ===&lt;br /&gt;
React Flow is a React library. That means React developers will feel comfortable using it. If basic React terms and concepts like states, props, components, and hooks are unfamiliar to you, you might need to learn more about React before being able to use React Flow fully.&lt;br /&gt;
&lt;br /&gt;
== Building a Flow ==&lt;br /&gt;
=== Getting Started ===&lt;br /&gt;
Let's create an empty flow with a controls panel and a background. For this we need to import the components from the reactflow package:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Background, Controls } from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can now use the components to render an empty flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Controls, Background } from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return (&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are three important things to keep in mind here:&lt;br /&gt;
* You need to import the styles. Otherwise, React Flow won't work.&lt;br /&gt;
* The parent container needs a width and height, because React Flow uses its parent dimensions.&lt;br /&gt;
* If you have multiple flows on one page, you need to pass a unique id prop to each component to make React Flow work properly.&lt;br /&gt;
&lt;br /&gt;
=== Adding Nodes ===&lt;br /&gt;
Now that the flow is set up, let's add some nodes. To do this, you need to create an array with node objects like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',  // required&lt;br /&gt;
    position: { x: 0, y: 0}, // required&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These nodes can now be added to the flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, {Controls, Background} from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow nodex={nodes}&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Let's add another node, configure labels and use the node type input for the first node.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
    data: {label: 'Hello'},&lt;br /&gt;
    type: 'input',&lt;br /&gt;
  },&lt;br /&gt;
  {&lt;br /&gt;
    id: '2',&lt;br /&gt;
    position: {x: 100, y: 100},&lt;br /&gt;
    data: { label: 'World' },&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3993</id>
		<title>React flow- Getting Started</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow-_Getting_Started&amp;diff=3993"/>
		<updated>2024-03-05T06:44:56Z</updated>

		<summary type="html">&lt;p&gt;Pchero: Created page with &amp;quot;== Overview == Reactflow 정리 문서 원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/getting-started/installation-and-requirements&amp;lt;/ref&amp;gt;에서 확인할 수 있다.  == Installation == === Installation and Requirements === The React Flow package is published under reactflow on npm and installable via:  &amp;lt;pre&amp;gt; $ npm install reactflow &amp;lt;/pre&amp;gt;  Now you can import the React Flow component and the styles in your application: &amp;lt;pre&amp;gt; import ReactFlow from 'reactflow'; import 'r...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Reactflow 정리 문서&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn/getting-started/installation-and-requirements&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
=== Installation and Requirements ===&lt;br /&gt;
The React Flow package is published under reactflow on npm and installable via:&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ npm install reactflow&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Now you can import the React Flow component and the styles in your application:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Prior Experience Needed ===&lt;br /&gt;
React Flow is a React library. That means React developers will feel comfortable using it. If basic React terms and concepts like states, props, components, and hooks are unfamiliar to you, you might need to learn more about React before being able to use React Flow fully.&lt;br /&gt;
&lt;br /&gt;
== Building a Flow ==&lt;br /&gt;
=== Getting Started ===&lt;br /&gt;
Let's create an empty flow with a controls panel and a background. For this we need to import the components from the reactflow package:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Background, Controls } from 'reactflow';&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We can now use the components to render an empty flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, { Controls, Background } from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return (&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
There are three important things to keep in mind here:&lt;br /&gt;
* You need to import the styles. Otherwise, React Flow won't work.&lt;br /&gt;
* The parent container needs a width and height, because React Flow uses its parent dimensions.&lt;br /&gt;
* If you have multiple flows on one page, you need to pass a unique id prop to each component to make React Flow work properly.&lt;br /&gt;
&lt;br /&gt;
=== Adding Nodes ===&lt;br /&gt;
Now that the flow is set up, let's add some nodes. To do this, you need to create an array with node objects like this:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',  // required&lt;br /&gt;
    position: { x: 0, y: 0}, // required&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These nodes can now be added to the flow:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
import ReactFlow, {Controls, Background} from 'reactflow';&lt;br /&gt;
import 'reactflow/dist/style.css';&lt;br /&gt;
&lt;br /&gt;
const nodes = [&lt;br /&gt;
  {&lt;br /&gt;
    id: '1',&lt;br /&gt;
    position: {x: 0, y: 0},&lt;br /&gt;
  },&lt;br /&gt;
];&lt;br /&gt;
&lt;br /&gt;
function Flow() {&lt;br /&gt;
  return(&lt;br /&gt;
    &amp;lt;div style={{ height: '100%' }}&amp;gt;&lt;br /&gt;
      &amp;lt;ReactFlow nodex={nodes}&amp;gt;&lt;br /&gt;
        &amp;lt;Background /&amp;gt;&lt;br /&gt;
        &amp;lt;Controls /&amp;gt;&lt;br /&gt;
      &amp;lt;/ReactFlow&amp;gt;&lt;br /&gt;
    &amp;lt;/div&amp;gt;&lt;br /&gt;
  );&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
export default Flow;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Category:React&amp;diff=3992</id>
		<title>Category:React</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Category:React&amp;diff=3992"/>
		<updated>2024-03-05T06:19:29Z</updated>

		<summary type="html">&lt;p&gt;Pchero: Created page with &amp;quot;category:programming&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[category:programming]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow&amp;diff=3991</id>
		<title>React flow</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow&amp;diff=3991"/>
		<updated>2024-03-05T06:18:17Z</updated>

		<summary type="html">&lt;p&gt;Pchero: Pchero moved page React flow to React flow - Concept&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;#REDIRECT [[React flow - Concept]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3990</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3990"/>
		<updated>2024-03-05T06:18:17Z</updated>

		<summary type="html">&lt;p&gt;Pchero: Pchero moved page React flow to React flow - Concept&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
Whenever React Flow triggers a change(node init, node drag, edge select, etc.), the onNodeChange handler gets called. The React Flow export an applyNodeChanges handler so that you don't need to handle the changes by yourself. The applyNodeChanges handler returns an updated array of nodes that is your new nodes array. It allows following kind of interactions.&lt;br /&gt;
* selectable nodes and edges.&lt;br /&gt;
* draggable nodes.&lt;br /&gt;
* removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the deleteKeyCode prop).&lt;br /&gt;
* multi-selection area by pressing Shift(that's the default selectionKeyCode).&lt;br /&gt;
* multi-selection by pressing command(that's the default multiSelectionKeyCode).&lt;br /&gt;
&lt;br /&gt;
For convenience we export the helper hooks useNodeState and useEdgeState that you can use to create the nodes and edges state:&lt;br /&gt;
```&lt;br /&gt;
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);&lt;br /&gt;
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
=== Connecting Nodes ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (connection) =&amp;gt;&lt;br /&gt;
    setEdges((eds) =&amp;gt; addEdge({ ...connection, animated: true }, eds)),&lt;br /&gt;
  [setEdges],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or use the defaultEdgeOptions prop:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const defaultEdgeOptions = { animated: true };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;ReactFlow&lt;br /&gt;
  nodes={nodes}&lt;br /&gt;
  edges={edges}&lt;br /&gt;
  onNodesChange={onNodesChange}&lt;br /&gt;
  onEdgesChange={onEdgesChange}&lt;br /&gt;
  onConnect={onConnect}&lt;br /&gt;
  defaultEdgeOptions={defaultEdgeOptions}&lt;br /&gt;
/&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Viewport ==&lt;br /&gt;
=== Panning and Zooming ===&lt;br /&gt;
The default pan and zoom behaviour of React Flow is inspired by slippy maps. You pan by dragging and zoom by scrolling. You can customize this behaviour easily with the provided props:&lt;br /&gt;
* panOnDrag: default: true&lt;br /&gt;
* selectionOnDrag: default: false&lt;br /&gt;
* panOnScroll: default: false&lt;br /&gt;
* panOnScrollSpeed: default: 0.5&lt;br /&gt;
* panOnScrollMode: default: 'free'. 'free'(all directions), 'vertical'(only vertical) or 'horizontal'(only horizontal).&lt;br /&gt;
* zoomOnScroll: default: true&lt;br /&gt;
* zoomOnPinch: default: true&lt;br /&gt;
* zoomOnDoubleClick: default: true&lt;br /&gt;
* preventScrolling: default: true&lt;br /&gt;
* zoomActivationKeyCode: default 'Meta'&lt;br /&gt;
* panActivationKeyCode: default 'Space'&lt;br /&gt;
&lt;br /&gt;
=== Default Viewport Control ===&lt;br /&gt;
Default controls are:&lt;br /&gt;
* pan: drag mouse&lt;br /&gt;
* zoom: scroll&lt;br /&gt;
* create selection: Shift + drag&lt;br /&gt;
&lt;br /&gt;
=== Figma-like Viewport Controls ===&lt;br /&gt;
If you prefer figma/sketch/design tool controls you can set panOnScroll={true} and selectionOnDrag={true};&lt;br /&gt;
* pan: Space + drag mouse, scroll, middle or right mouse.&lt;br /&gt;
* zoom: pitch or cmd + scroll&lt;br /&gt;
* create selection: drag mouse&lt;br /&gt;
&lt;br /&gt;
== Plugin Components ==&lt;br /&gt;
React Flow comes with several additional plugin components.&lt;br /&gt;
&lt;br /&gt;
=== MiniMap ===&lt;br /&gt;
If your flow gets bigger, you might want to get an overview quickly. For this we have built the MiniMap component. You can easily add it to your flow by adding it as a children.&lt;br /&gt;
&lt;br /&gt;
=== Controls ===&lt;br /&gt;
React Flow comes with a customizable controls bar, that you can use by importing the Controls component.&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
If you want to display the pattern background, you can use the Background component.&lt;br /&gt;
&lt;br /&gt;
=== Panel ===&lt;br /&gt;
A helper component to display content on top of the React Flow viewport.&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3989</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3989"/>
		<updated>2024-03-01T14:57:00Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Background */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
Whenever React Flow triggers a change(node init, node drag, edge select, etc.), the onNodeChange handler gets called. The React Flow export an applyNodeChanges handler so that you don't need to handle the changes by yourself. The applyNodeChanges handler returns an updated array of nodes that is your new nodes array. It allows following kind of interactions.&lt;br /&gt;
* selectable nodes and edges.&lt;br /&gt;
* draggable nodes.&lt;br /&gt;
* removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the deleteKeyCode prop).&lt;br /&gt;
* multi-selection area by pressing Shift(that's the default selectionKeyCode).&lt;br /&gt;
* multi-selection by pressing command(that's the default multiSelectionKeyCode).&lt;br /&gt;
&lt;br /&gt;
For convenience we export the helper hooks useNodeState and useEdgeState that you can use to create the nodes and edges state:&lt;br /&gt;
```&lt;br /&gt;
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);&lt;br /&gt;
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
=== Connecting Nodes ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (connection) =&amp;gt;&lt;br /&gt;
    setEdges((eds) =&amp;gt; addEdge({ ...connection, animated: true }, eds)),&lt;br /&gt;
  [setEdges],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or use the defaultEdgeOptions prop:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const defaultEdgeOptions = { animated: true };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;ReactFlow&lt;br /&gt;
  nodes={nodes}&lt;br /&gt;
  edges={edges}&lt;br /&gt;
  onNodesChange={onNodesChange}&lt;br /&gt;
  onEdgesChange={onEdgesChange}&lt;br /&gt;
  onConnect={onConnect}&lt;br /&gt;
  defaultEdgeOptions={defaultEdgeOptions}&lt;br /&gt;
/&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Viewport ==&lt;br /&gt;
=== Panning and Zooming ===&lt;br /&gt;
The default pan and zoom behaviour of React Flow is inspired by slippy maps. You pan by dragging and zoom by scrolling. You can customize this behaviour easily with the provided props:&lt;br /&gt;
* panOnDrag: default: true&lt;br /&gt;
* selectionOnDrag: default: false&lt;br /&gt;
* panOnScroll: default: false&lt;br /&gt;
* panOnScrollSpeed: default: 0.5&lt;br /&gt;
* panOnScrollMode: default: 'free'. 'free'(all directions), 'vertical'(only vertical) or 'horizontal'(only horizontal).&lt;br /&gt;
* zoomOnScroll: default: true&lt;br /&gt;
* zoomOnPinch: default: true&lt;br /&gt;
* zoomOnDoubleClick: default: true&lt;br /&gt;
* preventScrolling: default: true&lt;br /&gt;
* zoomActivationKeyCode: default 'Meta'&lt;br /&gt;
* panActivationKeyCode: default 'Space'&lt;br /&gt;
&lt;br /&gt;
=== Default Viewport Control ===&lt;br /&gt;
Default controls are:&lt;br /&gt;
* pan: drag mouse&lt;br /&gt;
* zoom: scroll&lt;br /&gt;
* create selection: Shift + drag&lt;br /&gt;
&lt;br /&gt;
=== Figma-like Viewport Controls ===&lt;br /&gt;
If you prefer figma/sketch/design tool controls you can set panOnScroll={true} and selectionOnDrag={true};&lt;br /&gt;
* pan: Space + drag mouse, scroll, middle or right mouse.&lt;br /&gt;
* zoom: pitch or cmd + scroll&lt;br /&gt;
* create selection: drag mouse&lt;br /&gt;
&lt;br /&gt;
== Plugin Components ==&lt;br /&gt;
React Flow comes with several additional plugin components.&lt;br /&gt;
&lt;br /&gt;
=== MiniMap ===&lt;br /&gt;
If your flow gets bigger, you might want to get an overview quickly. For this we have built the MiniMap component. You can easily add it to your flow by adding it as a children.&lt;br /&gt;
&lt;br /&gt;
=== Controls ===&lt;br /&gt;
React Flow comes with a customizable controls bar, that you can use by importing the Controls component.&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
If you want to display the pattern background, you can use the Background component.&lt;br /&gt;
&lt;br /&gt;
=== Panel ===&lt;br /&gt;
A helper component to display content on top of the React Flow viewport.&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3988</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3988"/>
		<updated>2024-03-01T14:48:56Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Controls */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
Whenever React Flow triggers a change(node init, node drag, edge select, etc.), the onNodeChange handler gets called. The React Flow export an applyNodeChanges handler so that you don't need to handle the changes by yourself. The applyNodeChanges handler returns an updated array of nodes that is your new nodes array. It allows following kind of interactions.&lt;br /&gt;
* selectable nodes and edges.&lt;br /&gt;
* draggable nodes.&lt;br /&gt;
* removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the deleteKeyCode prop).&lt;br /&gt;
* multi-selection area by pressing Shift(that's the default selectionKeyCode).&lt;br /&gt;
* multi-selection by pressing command(that's the default multiSelectionKeyCode).&lt;br /&gt;
&lt;br /&gt;
For convenience we export the helper hooks useNodeState and useEdgeState that you can use to create the nodes and edges state:&lt;br /&gt;
```&lt;br /&gt;
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);&lt;br /&gt;
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
=== Connecting Nodes ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (connection) =&amp;gt;&lt;br /&gt;
    setEdges((eds) =&amp;gt; addEdge({ ...connection, animated: true }, eds)),&lt;br /&gt;
  [setEdges],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or use the defaultEdgeOptions prop:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const defaultEdgeOptions = { animated: true };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;ReactFlow&lt;br /&gt;
  nodes={nodes}&lt;br /&gt;
  edges={edges}&lt;br /&gt;
  onNodesChange={onNodesChange}&lt;br /&gt;
  onEdgesChange={onEdgesChange}&lt;br /&gt;
  onConnect={onConnect}&lt;br /&gt;
  defaultEdgeOptions={defaultEdgeOptions}&lt;br /&gt;
/&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Viewport ==&lt;br /&gt;
=== Panning and Zooming ===&lt;br /&gt;
The default pan and zoom behaviour of React Flow is inspired by slippy maps. You pan by dragging and zoom by scrolling. You can customize this behaviour easily with the provided props:&lt;br /&gt;
* panOnDrag: default: true&lt;br /&gt;
* selectionOnDrag: default: false&lt;br /&gt;
* panOnScroll: default: false&lt;br /&gt;
* panOnScrollSpeed: default: 0.5&lt;br /&gt;
* panOnScrollMode: default: 'free'. 'free'(all directions), 'vertical'(only vertical) or 'horizontal'(only horizontal).&lt;br /&gt;
* zoomOnScroll: default: true&lt;br /&gt;
* zoomOnPinch: default: true&lt;br /&gt;
* zoomOnDoubleClick: default: true&lt;br /&gt;
* preventScrolling: default: true&lt;br /&gt;
* zoomActivationKeyCode: default 'Meta'&lt;br /&gt;
* panActivationKeyCode: default 'Space'&lt;br /&gt;
&lt;br /&gt;
=== Default Viewport Control ===&lt;br /&gt;
Default controls are:&lt;br /&gt;
* pan: drag mouse&lt;br /&gt;
* zoom: scroll&lt;br /&gt;
* create selection: Shift + drag&lt;br /&gt;
&lt;br /&gt;
=== Figma-like Viewport Controls ===&lt;br /&gt;
If you prefer figma/sketch/design tool controls you can set panOnScroll={true} and selectionOnDrag={true};&lt;br /&gt;
* pan: Space + drag mouse, scroll, middle or right mouse.&lt;br /&gt;
* zoom: pitch or cmd + scroll&lt;br /&gt;
* create selection: drag mouse&lt;br /&gt;
&lt;br /&gt;
== Plugin Components ==&lt;br /&gt;
React Flow comes with several additional plugin components.&lt;br /&gt;
&lt;br /&gt;
=== MiniMap ===&lt;br /&gt;
If your flow gets bigger, you might want to get an overview quickly. For this we have built the MiniMap component. You can easily add it to your flow by adding it as a children.&lt;br /&gt;
&lt;br /&gt;
=== Controls ===&lt;br /&gt;
React Flow comes with a customizable controls bar, that you can use by importing the Controls component.&lt;br /&gt;
&lt;br /&gt;
=== Background ===&lt;br /&gt;
If you want to display the pattern background, you can use the Background component.&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3987</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3987"/>
		<updated>2024-03-01T14:46:06Z</updated>

		<summary type="html">&lt;p&gt;Pchero: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
Whenever React Flow triggers a change(node init, node drag, edge select, etc.), the onNodeChange handler gets called. The React Flow export an applyNodeChanges handler so that you don't need to handle the changes by yourself. The applyNodeChanges handler returns an updated array of nodes that is your new nodes array. It allows following kind of interactions.&lt;br /&gt;
* selectable nodes and edges.&lt;br /&gt;
* draggable nodes.&lt;br /&gt;
* removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the deleteKeyCode prop).&lt;br /&gt;
* multi-selection area by pressing Shift(that's the default selectionKeyCode).&lt;br /&gt;
* multi-selection by pressing command(that's the default multiSelectionKeyCode).&lt;br /&gt;
&lt;br /&gt;
For convenience we export the helper hooks useNodeState and useEdgeState that you can use to create the nodes and edges state:&lt;br /&gt;
```&lt;br /&gt;
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);&lt;br /&gt;
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
=== Connecting Nodes ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (connection) =&amp;gt;&lt;br /&gt;
    setEdges((eds) =&amp;gt; addEdge({ ...connection, animated: true }, eds)),&lt;br /&gt;
  [setEdges],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or use the defaultEdgeOptions prop:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const defaultEdgeOptions = { animated: true };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;ReactFlow&lt;br /&gt;
  nodes={nodes}&lt;br /&gt;
  edges={edges}&lt;br /&gt;
  onNodesChange={onNodesChange}&lt;br /&gt;
  onEdgesChange={onEdgesChange}&lt;br /&gt;
  onConnect={onConnect}&lt;br /&gt;
  defaultEdgeOptions={defaultEdgeOptions}&lt;br /&gt;
/&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==The Viewport ==&lt;br /&gt;
=== Panning and Zooming ===&lt;br /&gt;
The default pan and zoom behaviour of React Flow is inspired by slippy maps. You pan by dragging and zoom by scrolling. You can customize this behaviour easily with the provided props:&lt;br /&gt;
* panOnDrag: default: true&lt;br /&gt;
* selectionOnDrag: default: false&lt;br /&gt;
* panOnScroll: default: false&lt;br /&gt;
* panOnScrollSpeed: default: 0.5&lt;br /&gt;
* panOnScrollMode: default: 'free'. 'free'(all directions), 'vertical'(only vertical) or 'horizontal'(only horizontal).&lt;br /&gt;
* zoomOnScroll: default: true&lt;br /&gt;
* zoomOnPinch: default: true&lt;br /&gt;
* zoomOnDoubleClick: default: true&lt;br /&gt;
* preventScrolling: default: true&lt;br /&gt;
* zoomActivationKeyCode: default 'Meta'&lt;br /&gt;
* panActivationKeyCode: default 'Space'&lt;br /&gt;
&lt;br /&gt;
=== Default Viewport Control ===&lt;br /&gt;
Default controls are:&lt;br /&gt;
* pan: drag mouse&lt;br /&gt;
* zoom: scroll&lt;br /&gt;
* create selection: Shift + drag&lt;br /&gt;
&lt;br /&gt;
=== Figma-like Viewport Controls ===&lt;br /&gt;
If you prefer figma/sketch/design tool controls you can set panOnScroll={true} and selectionOnDrag={true};&lt;br /&gt;
* pan: Space + drag mouse, scroll, middle or right mouse.&lt;br /&gt;
* zoom: pitch or cmd + scroll&lt;br /&gt;
* create selection: drag mouse&lt;br /&gt;
&lt;br /&gt;
== Plugin Components ==&lt;br /&gt;
React Flow comes with several additional plugin components.&lt;br /&gt;
&lt;br /&gt;
=== MiniMap ===&lt;br /&gt;
If your flow gets bigger, you might want to get an overview quickly. For this we have built the MiniMap component. You can easily add it to your flow by adding it as a children.&lt;br /&gt;
&lt;br /&gt;
=== Controls ===&lt;br /&gt;
React Flow comes with a customizable controls bar, that you can use by importing the Controls component.&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3986</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3986"/>
		<updated>2024-03-01T14:37:51Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* = Panning and Zooming */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
Whenever React Flow triggers a change(node init, node drag, edge select, etc.), the onNodeChange handler gets called. The React Flow export an applyNodeChanges handler so that you don't need to handle the changes by yourself. The applyNodeChanges handler returns an updated array of nodes that is your new nodes array. It allows following kind of interactions.&lt;br /&gt;
* selectable nodes and edges.&lt;br /&gt;
* draggable nodes.&lt;br /&gt;
* removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the deleteKeyCode prop).&lt;br /&gt;
* multi-selection area by pressing Shift(that's the default selectionKeyCode).&lt;br /&gt;
* multi-selection by pressing command(that's the default multiSelectionKeyCode).&lt;br /&gt;
&lt;br /&gt;
For convenience we export the helper hooks useNodeState and useEdgeState that you can use to create the nodes and edges state:&lt;br /&gt;
```&lt;br /&gt;
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);&lt;br /&gt;
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
=== Connecting Nodes ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (connection) =&amp;gt;&lt;br /&gt;
    setEdges((eds) =&amp;gt; addEdge({ ...connection, animated: true }, eds)),&lt;br /&gt;
  [setEdges],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or use the defaultEdgeOptions prop:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const defaultEdgeOptions = { animated: true };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;ReactFlow&lt;br /&gt;
  nodes={nodes}&lt;br /&gt;
  edges={edges}&lt;br /&gt;
  onNodesChange={onNodesChange}&lt;br /&gt;
  onEdgesChange={onEdgesChange}&lt;br /&gt;
  onConnect={onConnect}&lt;br /&gt;
  defaultEdgeOptions={defaultEdgeOptions}&lt;br /&gt;
/&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===The Viewport ===&lt;br /&gt;
==== Panning and Zooming ====&lt;br /&gt;
The default pan and zoom behaviour of React Flow is inspired by slippy maps. You pan by dragging and zoom by scrolling. You can customize this behaviour easily with the provided props:&lt;br /&gt;
* panOnDrag: default: true&lt;br /&gt;
* selectionOnDrag: default: false&lt;br /&gt;
* panOnScroll: default: false&lt;br /&gt;
* panOnScrollSpeed: default: 0.5&lt;br /&gt;
* panOnScrollMode: default: 'free'. 'free'(all directions), 'vertical'(only vertical) or 'horizontal'(only horizontal).&lt;br /&gt;
* zoomOnScroll: default: true&lt;br /&gt;
* zoomOnPinch: default: true&lt;br /&gt;
* zoomOnDoubleClick: default: true&lt;br /&gt;
* preventScrolling: default: true&lt;br /&gt;
* zoomActivationKeyCode: default 'Meta'&lt;br /&gt;
* panActivationKeyCode: default 'Space'&lt;br /&gt;
&lt;br /&gt;
==== Default Viewport Control ====&lt;br /&gt;
Default controls are:&lt;br /&gt;
* pan: drag mouse&lt;br /&gt;
* zoom: scroll&lt;br /&gt;
* create selection: Shift + drag&lt;br /&gt;
&lt;br /&gt;
==== Figma-like Viewport Controls ====&lt;br /&gt;
If you prefer figma/sketch/design tool controls you can set panOnScroll={true} and selectionOnDrag={true};&lt;br /&gt;
* pan: Space + drag mouse, scroll, middle or right mouse.&lt;br /&gt;
* zoom: pitch or cmd + scroll&lt;br /&gt;
* create selection: drag mouse&lt;br /&gt;
&lt;br /&gt;
=== Plugin Components ===&lt;br /&gt;
React Flow comes with several additional plugin components.&lt;br /&gt;
&lt;br /&gt;
==== MiniMap ====&lt;br /&gt;
If your flow gets bigger, you might want to get an overview quickly. For this we have built the MiniMap component. You can easily add it to your flow by adding it as a children.&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3985</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3985"/>
		<updated>2024-03-01T14:21:02Z</updated>

		<summary type="html">&lt;p&gt;Pchero: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
Whenever React Flow triggers a change(node init, node drag, edge select, etc.), the onNodeChange handler gets called. The React Flow export an applyNodeChanges handler so that you don't need to handle the changes by yourself. The applyNodeChanges handler returns an updated array of nodes that is your new nodes array. It allows following kind of interactions.&lt;br /&gt;
* selectable nodes and edges.&lt;br /&gt;
* draggable nodes.&lt;br /&gt;
* removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the deleteKeyCode prop).&lt;br /&gt;
* multi-selection area by pressing Shift(that's the default selectionKeyCode).&lt;br /&gt;
* multi-selection by pressing command(that's the default multiSelectionKeyCode).&lt;br /&gt;
&lt;br /&gt;
For convenience we export the helper hooks useNodeState and useEdgeState that you can use to create the nodes and edges state:&lt;br /&gt;
```&lt;br /&gt;
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);&lt;br /&gt;
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
=== Connecting Nodes ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (connection) =&amp;gt;&lt;br /&gt;
    setEdges((eds) =&amp;gt; addEdge({ ...connection, animated: true }, eds)),&lt;br /&gt;
  [setEdges],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or use the defaultEdgeOptions prop:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const defaultEdgeOptions = { animated: true };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;ReactFlow&lt;br /&gt;
  nodes={nodes}&lt;br /&gt;
  edges={edges}&lt;br /&gt;
  onNodesChange={onNodesChange}&lt;br /&gt;
  onEdgesChange={onEdgesChange}&lt;br /&gt;
  onConnect={onConnect}&lt;br /&gt;
  defaultEdgeOptions={defaultEdgeOptions}&lt;br /&gt;
/&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===The Viewport ===&lt;br /&gt;
==== Panning and Zooming ===&lt;br /&gt;
The default pan and zoom behaviour of React Flow is inspired by slippy maps. You pan by dragging and zoom by scrolling. You can customize this behaviour easily with the provided props:&lt;br /&gt;
* panOnDrag: default: true&lt;br /&gt;
* selectionOnDrag: default: false&lt;br /&gt;
* panOnScroll: default: false&lt;br /&gt;
* panOnScrollSpeed: default: 0.5&lt;br /&gt;
* panOnScrollMode: default: 'free'. 'free'(all directions), 'vertical'(only vertical) or 'horizontal'(only horizontal).&lt;br /&gt;
* zoomOnScroll: default: true&lt;br /&gt;
* zoomOnPinch: default: true&lt;br /&gt;
* zoomOnDoubleClick: default: true&lt;br /&gt;
* preventScrolling: default: true&lt;br /&gt;
* zoomActivationKeyCode: default 'Meta'&lt;br /&gt;
* panActivationKeyCode: default 'Space'&lt;br /&gt;
&lt;br /&gt;
==== Default Viewport Control ====&lt;br /&gt;
Default controls are:&lt;br /&gt;
* pan: drag mouse&lt;br /&gt;
* zoom: scroll&lt;br /&gt;
* create selection: Shift + drag&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3984</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3984"/>
		<updated>2024-03-01T14:16:40Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Connecting Nodes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
Whenever React Flow triggers a change(node init, node drag, edge select, etc.), the onNodeChange handler gets called. The React Flow export an applyNodeChanges handler so that you don't need to handle the changes by yourself. The applyNodeChanges handler returns an updated array of nodes that is your new nodes array. It allows following kind of interactions.&lt;br /&gt;
* selectable nodes and edges.&lt;br /&gt;
* draggable nodes.&lt;br /&gt;
* removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the deleteKeyCode prop).&lt;br /&gt;
* multi-selection area by pressing Shift(that's the default selectionKeyCode).&lt;br /&gt;
* multi-selection by pressing command(that's the default multiSelectionKeyCode).&lt;br /&gt;
&lt;br /&gt;
For convenience we export the helper hooks useNodeState and useEdgeState that you can use to create the nodes and edges state:&lt;br /&gt;
```&lt;br /&gt;
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);&lt;br /&gt;
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
=== Connecting Nodes ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (connection) =&amp;gt;&lt;br /&gt;
    setEdges((eds) =&amp;gt; addEdge({ ...connection, animated: true }, eds)),&lt;br /&gt;
  [setEdges],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or use the defaultEdgeOptions prop:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const defaultEdgeOptions = { animated: true };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;ReactFlow&lt;br /&gt;
  nodes={nodes}&lt;br /&gt;
  edges={edges}&lt;br /&gt;
  onNodesChange={onNodesChange}&lt;br /&gt;
  onEdgesChange={onEdgesChange}&lt;br /&gt;
  onConnect={onConnect}&lt;br /&gt;
  defaultEdgeOptions={defaultEdgeOptions}&lt;br /&gt;
/&amp;gt;;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3983</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3983"/>
		<updated>2024-03-01T14:16:22Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Connecting Nodes */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
Whenever React Flow triggers a change(node init, node drag, edge select, etc.), the onNodeChange handler gets called. The React Flow export an applyNodeChanges handler so that you don't need to handle the changes by yourself. The applyNodeChanges handler returns an updated array of nodes that is your new nodes array. It allows following kind of interactions.&lt;br /&gt;
* selectable nodes and edges.&lt;br /&gt;
* draggable nodes.&lt;br /&gt;
* removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the deleteKeyCode prop).&lt;br /&gt;
* multi-selection area by pressing Shift(that's the default selectionKeyCode).&lt;br /&gt;
* multi-selection by pressing command(that's the default multiSelectionKeyCode).&lt;br /&gt;
&lt;br /&gt;
For convenience we export the helper hooks useNodeState and useEdgeState that you can use to create the nodes and edges state:&lt;br /&gt;
```&lt;br /&gt;
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);&lt;br /&gt;
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
=== Connecting Nodes ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (connection) =&amp;gt;&lt;br /&gt;
    setEdges((eds) =&amp;gt; addEdge({ ...connection, animated: true }, eds)),&lt;br /&gt;
  [setEdges],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or use the defaultEdgeOptions prop:&lt;br /&gt;
```&lt;br /&gt;
const defaultEdgeOptions = { animated: true };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;ReactFlow&lt;br /&gt;
  nodes={nodes}&lt;br /&gt;
  edges={edges}&lt;br /&gt;
  onNodesChange={onNodesChange}&lt;br /&gt;
  onEdgesChange={onEdgesChange}&lt;br /&gt;
  onConnect={onConnect}&lt;br /&gt;
  defaultEdgeOptions={defaultEdgeOptions}&lt;br /&gt;
/&amp;gt;;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3982</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3982"/>
		<updated>2024-03-01T14:16:03Z</updated>

		<summary type="html">&lt;p&gt;Pchero: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
Whenever React Flow triggers a change(node init, node drag, edge select, etc.), the onNodeChange handler gets called. The React Flow export an applyNodeChanges handler so that you don't need to handle the changes by yourself. The applyNodeChanges handler returns an updated array of nodes that is your new nodes array. It allows following kind of interactions.&lt;br /&gt;
* selectable nodes and edges.&lt;br /&gt;
* draggable nodes.&lt;br /&gt;
* removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the deleteKeyCode prop).&lt;br /&gt;
* multi-selection area by pressing Shift(that's the default selectionKeyCode).&lt;br /&gt;
* multi-selection by pressing command(that's the default multiSelectionKeyCode).&lt;br /&gt;
&lt;br /&gt;
For convenience we export the helper hooks useNodeState and useEdgeState that you can use to create the nodes and edges state:&lt;br /&gt;
```&lt;br /&gt;
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);&lt;br /&gt;
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
=== Connecting Nodes ===&lt;br /&gt;
```&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (connection) =&amp;gt;&lt;br /&gt;
    setEdges((eds) =&amp;gt; addEdge({ ...connection, animated: true }, eds)),&lt;br /&gt;
  [setEdges],&lt;br /&gt;
);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
or use the defaultEdgeOptions prop:&lt;br /&gt;
```&lt;br /&gt;
const defaultEdgeOptions = { animated: true };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;ReactFlow&lt;br /&gt;
  nodes={nodes}&lt;br /&gt;
  edges={edges}&lt;br /&gt;
  onNodesChange={onNodesChange}&lt;br /&gt;
  onEdgesChange={onEdgesChange}&lt;br /&gt;
  onConnect={onConnect}&lt;br /&gt;
  defaultEdgeOptions={defaultEdgeOptions}&lt;br /&gt;
/&amp;gt;;&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3981</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3981"/>
		<updated>2024-03-01T14:15:06Z</updated>

		<summary type="html">&lt;p&gt;Pchero: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
Whenever React Flow triggers a change(node init, node drag, edge select, etc.), the onNodeChange handler gets called. The React Flow export an applyNodeChanges handler so that you don't need to handle the changes by yourself. The applyNodeChanges handler returns an updated array of nodes that is your new nodes array. It allows following kind of interactions.&lt;br /&gt;
* selectable nodes and edges.&lt;br /&gt;
* draggable nodes.&lt;br /&gt;
* removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the deleteKeyCode prop).&lt;br /&gt;
* multi-selection area by pressing Shift(that's the default selectionKeyCode).&lt;br /&gt;
* multi-selection by pressing command(that's the default multiSelectionKeyCode).&lt;br /&gt;
&lt;br /&gt;
For convenience we export the helper hooks useNodeState and useEdgeState that you can use to create the nodes and edges state:&lt;br /&gt;
```&lt;br /&gt;
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);&lt;br /&gt;
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
=== Connecting Nodes ===&lt;br /&gt;
&amp;lt;code lang=javascript&amp;gt;&lt;br /&gt;
const onConnect = useCallback(&lt;br /&gt;
  (connection) =&amp;gt;&lt;br /&gt;
    setEdges((eds) =&amp;gt; addEdge({ ...connection, animated: true }, eds)),&lt;br /&gt;
  [setEdges],&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
or use the defaultEdgeOptions prop:&lt;br /&gt;
&amp;lt;code lang=javascript&amp;gt;&lt;br /&gt;
const defaultEdgeOptions = { animated: true };&lt;br /&gt;
...&lt;br /&gt;
&amp;lt;ReactFlow&lt;br /&gt;
  nodes={nodes}&lt;br /&gt;
  edges={edges}&lt;br /&gt;
  onNodesChange={onNodesChange}&lt;br /&gt;
  onEdgesChange={onEdgesChange}&lt;br /&gt;
  onConnect={onConnect}&lt;br /&gt;
  defaultEdgeOptions={defaultEdgeOptions}&lt;br /&gt;
/&amp;gt;;&lt;br /&gt;
&amp;lt;/code&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3980</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3980"/>
		<updated>2024-03-01T13:09:35Z</updated>

		<summary type="html">&lt;p&gt;Pchero: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
Whenever React Flow triggers a change(node init, node drag, edge select, etc.), the onNodeChange handler gets called. The React Flow export an applyNodeChanges handler so that you don't need to handle the changes by yourself. The applyNodeChanges handler returns an updated array of nodes that is your new nodes array. It allows following kind of interactions.&lt;br /&gt;
* selectable nodes and edges.&lt;br /&gt;
* draggable nodes.&lt;br /&gt;
* removable nodes and edges - (press Backspace to remove a selected node or edge, can be adjusted with the deleteKeyCode prop).&lt;br /&gt;
* multi-selection area by pressing Shift(that's the default selectionKeyCode).&lt;br /&gt;
* multi-selection by pressing command(that's the default multiSelectionKeyCode).&lt;br /&gt;
&lt;br /&gt;
For convenience we export the helper hooks useNodeState and useEdgeState that you can use to create the nodes and edges state:&lt;br /&gt;
```&lt;br /&gt;
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);&lt;br /&gt;
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);&lt;br /&gt;
```&lt;br /&gt;
&lt;br /&gt;
=== Connecting Nodes ===&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3979</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3979"/>
		<updated>2024-03-01T12:43:03Z</updated>

		<summary type="html">&lt;p&gt;Pchero: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Viewport ===&lt;br /&gt;
All of React Flow exists inside of the viewport. The viewport has a x, y and zoom value. When you drag the pane, you change the x and y coordinates and when you zoom in or out you alter the zoom level.&lt;br /&gt;
&lt;br /&gt;
== Core Concepts ==&lt;br /&gt;
A flow consists of nodes and edges(or just edges). You can pass arrays of nodes and edges as props to the ReactFlow component. Hereby all node and edge ids need to be unique. A node needs a position and a label(this could be different if you using custom nodes) and an edge needs a source(node id) and a target(node id). You can read more about the options in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; and Edge options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge&amp;lt;/ref&amp;gt; sections.&lt;br /&gt;
&lt;br /&gt;
=== Controlled or Uncontrolled ===&lt;br /&gt;
With React Flow you have two ways to setup a flow. You can either create a controlled or an uncontrolled one. We recommend to use a controlled one but for simpler use cases you can also setup an uncontrolled flow.&lt;br /&gt;
&lt;br /&gt;
=== Basic Functionality ===&lt;br /&gt;
By default, React Flow doesn't do any internal state updates besides handling the viewport when you setup a controlled flow. As with an &amp;lt;input /&amp;gt; component you need to pass handlers to apply the changes that are triggered by React Flow to your nodes and edges. In order to select, drag and remove nodes and edges you need to implement an onNodesChange and an onEdgesChange handler.&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3978</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3978"/>
		<updated>2024-03-01T12:14:21Z</updated>

		<summary type="html">&lt;p&gt;Pchero: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
==== Custom Nodes ====&lt;br /&gt;
This is where the magic of React Flow happens. You can customize nodes to look and act however you would like. Much of the functionality that you might create is not built-in to React Flow. Some of the things you might do with a custom node are:&lt;br /&gt;
* Render form elements&lt;br /&gt;
* Visualize data&lt;br /&gt;
* Support multiple handles.&lt;br /&gt;
&lt;br /&gt;
=== Handles ===&lt;br /&gt;
A handle(also called &amp;quot;port&amp;quot; in other libraries) is the place where an edge attaches to a node. The handle can be placed anywhere, and styled as you like. It's just a div element. By default, it appears as a grey circle on the top, bottom, left, or fight of the nodes you can has as many handles as you need in your node. More information can be found in the handle docs&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/components/handle&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
=== Edges ===&lt;br /&gt;
An edge connects two nodes. Every edge needs a target and a source node. React Flow comes with four built-in edge typee:default(bezier), smoothstep, step and straight. An edge is SVG path that can be styled with OSS and is completely customizable. If you are using multiple handles, you can reference them individually to create multiple connections for a node.&lt;br /&gt;
&lt;br /&gt;
==== Custom Edges ====&lt;br /&gt;
Like custom nodes, you can also customize edges. Things that people do with custom edges are:&lt;br /&gt;
* Add a button to remove an edge.&lt;br /&gt;
* Custom routing behaviour.&lt;br /&gt;
* Complex styling or interactions that cannot be solved with just one SVG path.&lt;br /&gt;
&lt;br /&gt;
You can find more information on the custom edges api&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/edge-props&amp;lt;/ref&amp;gt; site.&lt;br /&gt;
&lt;br /&gt;
=== Connection Line ===&lt;br /&gt;
React Flow has built-in functionality to click-and-drag from one handle to another in order to create a new edge. While dragging, the placeholder edge is called a connection line. The connection line also comes with four types built-in and is customizable. You can find the props for configuring the connection in the props section&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/react-flow#connection-line&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3977</id>
		<title>React flow - Concept</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=React_flow_-_Concept&amp;diff=3977"/>
		<updated>2024-03-01T12:03:10Z</updated>

		<summary type="html">&lt;p&gt;Pchero: Created page with &amp;quot;== Overview == React flow 내용 정리. 원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.  == Introduction == === Key features === Buillt-in plugins * The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns. * The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen. * The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport. * The &amp;lt;Panel /&amp;gt; plugin makes it easy...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
React flow 내용 정리.&lt;br /&gt;
원문은 이곳&amp;lt;ref&amp;gt;https://reactflow.dev/learn&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
== Introduction ==&lt;br /&gt;
=== Key features ===&lt;br /&gt;
Buillt-in plugins&lt;br /&gt;
* The &amp;lt;Background /&amp;gt; plugin implements some basic customisable background patterns.&lt;br /&gt;
* The &amp;lt;MiniMap /&amp;gt; plugin displays a small version of the graph in the corner of the screen.&lt;br /&gt;
* The &amp;lt;Controls /&amp;gt; plugin adds controls to zoom, center, and lock the viewport.&lt;br /&gt;
* The &amp;lt;Panel /&amp;gt; plugin makes it easy to position content on top of the viewport.&lt;br /&gt;
* The &amp;lt;NodeToolbar /&amp;gt; plugin lets you render a toolbar attached to a node.&lt;br /&gt;
* The &amp;lt;NodeResizer /&amp;gt; plugin makes it easy to add resize functionality to your nodes.&lt;br /&gt;
&lt;br /&gt;
== Definitions ==&lt;br /&gt;
=== Nodes ===&lt;br /&gt;
A node in React Flow is a React component. That means it can render anything you like. Each node has an x- and y-coordinate, which tells it where it is placed in the viewport. You can find all the options for customizing your nodes in the Node options&amp;lt;ref&amp;gt;https://reactflow.dev/api-reference/types/node&amp;lt;/ref&amp;gt; documentation.&lt;br /&gt;
&lt;br /&gt;
[[category:react]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Asterisk_pjsip_sip_message&amp;diff=3976</id>
		<title>Asterisk pjsip sip message</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Asterisk_pjsip_sip_message&amp;diff=3976"/>
		<updated>2023-10-04T12:53:20Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* INVITE */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Asterisk pjsip module's sip message 관련 내용 정리&lt;br /&gt;
&lt;br /&gt;
== INVITE ==&lt;br /&gt;
Asterisk 에서의 모든 Inbound 채널 및 관련 세션 생성은 INVITE 메시지로부터 시작한다.&lt;br /&gt;
&lt;br /&gt;
=== Caller info ===&lt;br /&gt;
PJSIP 모듈은 inbound 콜 수행시, INVITE 메시지로부터 채널을 생성하면서, Caller 의 정보를 설정(Parsing)한다.&lt;br /&gt;
&lt;br /&gt;
Caller 정보를 설정할 때 사용하는 SIP header 는 여러가지가 있는데, 다음의 우선 순위로 파싱을 한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
1. P-Asserted-Identity&lt;br /&gt;
2. Remote-Party-ID&lt;br /&gt;
3. FROM&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
즉, 제일 먼저 P-Asserted-Identity 를 확인하고, 설정되어 있지 않다면, Remote-Party-ID 를 확인한다. 그리고 Remote-Party-ID 가 설정되어 있지 않다면 마지막으로 FROM 헤더를 확인하다.&lt;br /&gt;
&lt;br /&gt;
파싱되는 내용은 다음 과 같다.&lt;br /&gt;
Display name 을 파싱해서 Caller name 으로 사용하고, Username 을 파싱해서 Caller number 로 사용한다.&lt;br /&gt;
&lt;br /&gt;
예를 들어 P-Asserted identity 헤더가 다음과 같이 설정 되었다고 하자.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
P-Asserted-Identity=&amp;quot;test &amp;lt;1234@test.com&amp;gt;&amp;quot;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
그렇다면 다음과 같이 파싱이 된다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Caller name: test&lt;br /&gt;
Caller number: 1234&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
자세한 함수는 &lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
res/res_pjsip_caller_id.c &lt;br /&gt;
static int caller_id_incoming_request(struct ast_sip_session *session, pjsip_rx_data *rdata)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
에서 수행한다.&lt;br /&gt;
[[Category:Asterisk]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Sipexer&amp;diff=3975</id>
		<title>Sipexer</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Sipexer&amp;diff=3975"/>
		<updated>2023-09-27T16:51:33Z</updated>

		<summary type="html">&lt;p&gt;Pchero: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
sipexer - Modern and flexible SIP (RFC3261) command line tool 내용 정리.&lt;br /&gt;
&lt;br /&gt;
== OPTIONS ping ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# /usr/local/bin/sipexer -nagios -fu heartbeat 10.75.0.42:5060&lt;br /&gt;
&lt;br /&gt;
[info] [sipexer.go:1578] main.SIPExerDialogLoop(): local socket address: 10.75.0.42:36617 (udp)&lt;br /&gt;
[info] [sipexer.go:1579] main.SIPExerDialogLoop(): local via address: 10.75.0.42:36617&lt;br /&gt;
[info] [sipexer.go:1580] main.SIPExerDialogLoop(): sending to udp 10.75.0.42:5060: [[---&lt;br /&gt;
OPTIONS sip:10.75.0.42:5060 SIP/2.0&lt;br /&gt;
Via: SIP/2.0/UDP 10.75.0.42:36617;rport;branch=z9hG4bKSG.3b6e77cc-dbf8-4f58-9809-7bca9c3f9272&lt;br /&gt;
From: &amp;lt;sip:heartbeat@localhost&amp;gt;;tag=2d5b7fec-6f21-45e6-a20c-b2ae39535657&lt;br /&gt;
To: &amp;lt;sip:bob@localhost&amp;gt;&lt;br /&gt;
Call-ID: 9a11ff73-de7a-4a19-a0e4-38fa8757ea66&lt;br /&gt;
CSeq: 514429 OPTIONS&lt;br /&gt;
Date: Wed, 30 Aug 2023 00:40:44 UTC&lt;br /&gt;
User-Agent: SIPExer v1.1.0&lt;br /&gt;
Max-Forwards: 10&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[info] [sipexer.go:1582] main.SIPExerDialogLoop(): ---]]&lt;br /&gt;
&lt;br /&gt;
[info] [sipexer.go:1633] main.SIPExerDialogLoop(): response-received: from=10.75.0.42:5060 bytes=438 data=[[---&lt;br /&gt;
SIP/2.0 200 OK&lt;br /&gt;
Via: SIP/2.0/UDP 10.75.0.42:36617;rport=36617;branch=z9hG4bKSG.3b6e77cc-dbf8-4f58-9809-7bca9c3f9272;received=10.75.0.42&lt;br /&gt;
From: &amp;lt;sip:heartbeat@localhost&amp;gt;;tag=2d5b7fec-6f21-45e6-a20c-b2ae39535657&lt;br /&gt;
To: &amp;lt;sip:bob@localhost&amp;gt;;tag=8cc31d58288b2b802886d8395a3eac46.7aa55e46&lt;br /&gt;
Call-ID: 9a11ff73-de7a-4a19-a0e4-38fa8757ea66&lt;br /&gt;
CSeq: 514429 OPTIONS&lt;br /&gt;
Accept: */*&lt;br /&gt;
Accept-Encoding: &lt;br /&gt;
Accept-Language: en&lt;br /&gt;
Supported: &lt;br /&gt;
Content-Length: 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[info] [sipexer.go:1635] main.SIPExerDialogLoop(): ---]]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Registration ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ sipexer -register -vl 3 -co -com -ex 60 -fuser alice -cb -ap &amp;quot;abab...&amp;quot; -ha1 -sd udp:sipserver.com:5060&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://github.com/miconda/sipexer&lt;br /&gt;
&lt;br /&gt;
[[category:command/utility]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Kamailio_module_websocket&amp;diff=3974</id>
		<title>Kamailio module websocket</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Kamailio_module_websocket&amp;diff=3974"/>
		<updated>2023-09-21T05:51:48Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Overview */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Kamailio's WebSocket 모듈 내용 정리.&lt;br /&gt;
&lt;br /&gt;
The WebSocket module in Kamailio implemented a WebSocket(RFC 6455) server and provides connection establishment(handshaking), management(including connection keep-alive), and framing for the SIP and MSRP WebSocket sub-protocols (RFC 7118 and RFC 7977).&lt;br /&gt;
&lt;br /&gt;
== Parameters ==&lt;br /&gt;
=== keepalive_mechanism(integer) ===&lt;br /&gt;
The keep-alive mechanism to use for WebSocket connection.&lt;br /&gt;
&lt;br /&gt;
:&amp;lt;b&amp;gt;Note&amp;lt;/b&amp;gt;&lt;br /&gt;
:If nathelper is only being used for WebSocket connections then nathelper NAT pinging is not required. If nathelper is used for WebSocket connections and TCP/TLS aliasing/NAT-traversal then WebSocket keep-alives are not required.&lt;br /&gt;
* 0: no WebSocket keep-alives.&lt;br /&gt;
* 1: Ping WebSocket keep-alives.&lt;br /&gt;
* 2: Pong WebSocket keep-alives.&lt;br /&gt;
&lt;br /&gt;
Default value is 1.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
modparam(&amp;quot;websocket&amp;quot;, &amp;quot;keepalive_mechanism&amp;quot;, 0)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== keepalive_timeout(integer) ===&lt;br /&gt;
The time(in seconds) after which to send a keep-alive on idle WebSocket connections.&lt;br /&gt;
Default value is 180.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
modparam(&amp;quot;websocket&amp;quot;, &amp;quot;keepalive_timeout&amp;quot;, 50)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== keepalive_processes(integer) ===&lt;br /&gt;
The number of processes to start to perform WebSocket connection keep-alives.&lt;br /&gt;
&lt;br /&gt;
Default value is 1.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
modparam(&amp;quot;websocket&amp;quot;, &amp;quot;keepalive_processes&amp;quot;, 2)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== verbose_list(integer) ===&lt;br /&gt;
Allows to enable/disable the printing of debug messages when getting the list of websocket connections. If enabled, it prints debug messages every second for ping operations.&lt;br /&gt;
&lt;br /&gt;
Default value is 0(disabled).&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
modparam(&amp;quot;websocket&amp;quot;, &amp;quot;verbose_list&amp;quot;, 1)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://www.kamailio.org/docs/modules/devel/modules/websocket.html&lt;br /&gt;
&lt;br /&gt;
[[category:kamailio]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Kamailio_module_websocket&amp;diff=3973</id>
		<title>Kamailio module websocket</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Kamailio_module_websocket&amp;diff=3973"/>
		<updated>2023-09-21T05:38:22Z</updated>

		<summary type="html">&lt;p&gt;Pchero: Created page with &amp;quot;== Overview == Kamailio's WebSocket 모듈 내용 정리.  The WebSocket module in Kamailio implemented a WebSocket(RFC 6455) server and provides connection establishment(handshaking), management(including connection keep-alive), and framing for the SIP and MSRP WebSocket sub-protocols (RFC 7118 and RFC 7977).  == See also == * https://www.kamailio.org/docs/modules/devel/modules/websocket.html  category:kamailio&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Kamailio's WebSocket 모듈 내용 정리.&lt;br /&gt;
&lt;br /&gt;
The WebSocket module in Kamailio implemented a WebSocket(RFC 6455) server and provides connection establishment(handshaking), management(including connection keep-alive), and framing for the SIP and MSRP WebSocket sub-protocols (RFC 7118 and RFC 7977).&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://www.kamailio.org/docs/modules/devel/modules/websocket.html&lt;br /&gt;
&lt;br /&gt;
[[category:kamailio]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=SIP_SDP&amp;diff=3972</id>
		<title>SIP SDP</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=SIP_SDP&amp;diff=3972"/>
		<updated>2023-09-19T03:32:30Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Delayed(Late) Media in SDP (RFC 3959, RFC 3960) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
참조 원문은 이곳&amp;lt;ref&amp;gt;http://www.nexpert.net/497&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;http://www.nexpert.net/142&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
SDP는 코덱 협상과 같은 Capability Exchange 를 수행하는 프로토콜로 SIP 뿐만 아니라 MGCP와 Megaco에서도 사용한다. 기술적으로는 SIP와 SDP 프로토콜을 구분해서 사용하지만, SIP를 살펴볼 때 SDP는 자연스럽게 언급된다.&lt;br /&gt;
&lt;br /&gt;
== SDP 의 개요 ==&lt;br /&gt;
SDP는 Session Description Protocol 의 약어로 멀티미디어 세션 파라미터를 협상하는 프로토콜이다. SDP는 RFC 2327을 개정한 RFC 4566 으로 권고되었다. H.323에 대한 이해가 있다면 SDP가 H.323 프로토콜의 H.245와 비슷한 역할을 수행한다고 생각하면 된다.&lt;br /&gt;
&lt;br /&gt;
SIP를 요청과 응답 모델(Request &amp;amp; Response Model)로 정의하였듯이 SDP는 제안과 수락 모델(Offer &amp;amp; Answer Model)로 정의한다. SDP의 Offer/Answer Model 로의 동작에 대해서는 RFC 33264 An Offer/Answer Model with the SDP 에서 자세히 설명되어 있다.&lt;br /&gt;
&lt;br /&gt;
[[File:Sdp dialog.png]]&lt;br /&gt;
&lt;br /&gt;
SDP는 Capability를 협상하기 위해 기존의 호 처리 프로토콜을 이용한다. 위의 그림에서처럼 SDP는 SIP 메시지와 함께 전달된다. SIP INVITE 메시지에 SDP Offer가 포함되거나 200 OK 응답 메시지에 SDP Answer 가 포함된다.&lt;br /&gt;
&lt;br /&gt;
== SDP 메시지 분석 ==&lt;br /&gt;
SDP는 멀티미디어를 전달하는 RTP 프로토콜에 대한 세부적인 내용을 협상한다. SDP는 아래와 같이 SIP와 다른 메시지 포멧을 유지하지만, SIP와 같이 텍스트 기반으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
=== Session Description ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v= protocal version.&lt;br /&gt;
o= owner/creator and session identification.&lt;br /&gt;
s= session name.&lt;br /&gt;
i= (optional) session information.&lt;br /&gt;
u= (optional) URI of description.&lt;br /&gt;
e= (optional) email address - contact detail.&lt;br /&gt;
p= (optional) phone number - contact detail.&lt;br /&gt;
c= (optional) connection information - not required if included in media description.&lt;br /&gt;
b= (optional) session bandwidth information.&lt;br /&gt;
z= (optional) time zone adjustments.&lt;br /&gt;
k= (optional) encryption key.&lt;br /&gt;
a= (optional) zero or more session attribute lines.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* v=&amp;lt;version&amp;gt;&lt;br /&gt;
: Specifies the version of Session Description Protocol. As specified in RFC 4566, up to now there is only one version, which is Version 0. No minor versions exist.&lt;br /&gt;
&lt;br /&gt;
* o=&amp;lt;username&amp;gt;&amp;lt;sess-id&amp;gt;&amp;lt;sess-version&amp;gt;&amp;lt;nettype&amp;gt;&amp;lt;addrtype&amp;gt;&amp;lt;unicast-address&amp;gt;&lt;br /&gt;
: Details about the originator and identification of the session.&lt;br /&gt;
: &amp;lt;username&amp;gt;: The user's login. The MUST NOT contain spaces.&lt;br /&gt;
: &amp;lt;sess-id&amp;gt;: A numeric string used as unique identifier for the session.&lt;br /&gt;
: &amp;lt;sess-version&amp;gt;: A numeric string used as version number for this session description.&lt;br /&gt;
&lt;br /&gt;
=== Time description ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t= time the session is active.&lt;br /&gt;
r= (optional) repeat times.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Media description&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
m= media name/transport address.&lt;br /&gt;
i= (optional) media title&lt;br /&gt;
c= (optional) connection information - not required if included in session description.&lt;br /&gt;
b= (optional) bandwidth information.&lt;br /&gt;
k= (optional) encryption key.&lt;br /&gt;
a= (optional) zero or more media attribute lines.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844526 IN IP4 atlanta.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 10.1.3.33&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49172 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
각 라인의 의미는 다음과 같다.&lt;br /&gt;
&lt;br /&gt;
* v=0 (필수)&lt;br /&gt;
: SDP 프로토콜의 버전을 표시한다. SDP 버전은 0이다.&lt;br /&gt;
&lt;br /&gt;
* o=alice 2890844526 2890844526 IN IP4 atlanta.com (필수)&lt;br /&gt;
: SDP 메시지를 생성한 Owner/Creator 를 표시한다. 순서대로 Username, Session-ID, Session Version, Network Type, Address Type, Unicast Address 를 표시한다.&lt;br /&gt;
&lt;br /&gt;
* s= (필수)&lt;br /&gt;
: 세션 이름을 표시한다.&lt;br /&gt;
&lt;br /&gt;
* i=(optional)&lt;br /&gt;
: Session information.&lt;br /&gt;
&lt;br /&gt;
* u=(optional)&lt;br /&gt;
: URI of description.&lt;br /&gt;
&lt;br /&gt;
* e=(optional)&lt;br /&gt;
: Email address - contact detail.&lt;br /&gt;
&lt;br /&gt;
* p=(optional)&lt;br /&gt;
: Phone number - contact detail.&lt;br /&gt;
&lt;br /&gt;
* c=IN IP4 10.1.3.33 (optional)&lt;br /&gt;
: 순서대로 Network Type, Address Type, Connection-Address 를 나타내며 미디어의 주소를 정의한다.&lt;br /&gt;
&lt;br /&gt;
* t=0 0 (필수)&lt;br /&gt;
: Timing 으로 Start-time과 End-Time을 표시한다. 0 0 은 고정 세션을 의미한다.&lt;br /&gt;
&lt;br /&gt;
SDP의 Capability Exchange 를 주도하는 라인은 m= 와 a= 로 RTP가 사용할 코덱, IP 주소, 포트넘버가 자세히 명기된다. SDP를 생성하는 UA는 자신이 지원가능한 모든 코덱과 능력을 아래와 같이 명기한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
m=audio 16444 RTP/AVP 0 8 18 101&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
a=ptime:20&lt;br /&gt;
a=rtpmap:8 PCMA/8000&lt;br /&gt;
a=ptime:20&lt;br /&gt;
a=rtpmap:18 G729/8000&lt;br /&gt;
a=ptime:20&lt;br /&gt;
a=sendrecv&lt;br /&gt;
a=rtpmap:101 telephone-event/8000&lt;br /&gt;
a=fmtp:101 0-15 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* m= (미디어 설명)&lt;br /&gt;
: Media Description으로 Media, Port, Protocol, Format 을 정의한다.&lt;br /&gt;
: Media : audio, video, text, application, message 로 표시.&lt;br /&gt;
: Port : 미디어가 전송될 전송포트(Transport port) 표시.&lt;br /&gt;
: Protocol : UDP, RTP/AVP, RTP/SAVP로 표시하며 AVP는 Audio Video Profile의 약자이다.&lt;br /&gt;
: Format : 미디어의 포멧을 서브필드 (a=)로 표시함을 의미.&lt;br /&gt;
: Payload Type 0 8 18 의 순서는 코덱 협상의 우선순위를 나타내며, Payload Type 101은 DTMF 이벤트를 정의한다. 각 포멧에 대한 상세 설명은 a= 에 표시된다.&lt;br /&gt;
&lt;br /&gt;
* a= (미디어 속성)&lt;br /&gt;
: 미디어 속성(attributer)을 정의한다.&lt;br /&gt;
: a=rtpmap : Payload type, encoding name/clock rate 를 표시&lt;br /&gt;
: a=ptime : Packet time으로 미디어 패킷 한 개가 포함된 시간 정보로 ms로 표시 (보통 20ms).&lt;br /&gt;
: a=fmtp : 미디어 포멧에 대한 파라미터를 정의&lt;br /&gt;
&lt;br /&gt;
* a= (미디어의 방향)&lt;br /&gt;
: 미디어 속성 뿐만 아니라 미디어 방향도 표시한다. 미디어의 방향은 아래와 같이 4가지로 나타낸다.&lt;br /&gt;
: a=sendrecv : 단말은 미디어를 송신 및 수신할 수 있음.&lt;br /&gt;
: a=recvonly : 단말은 수신만을 할 수 있으며 송신하지 않음.&lt;br /&gt;
: a=sendonly : 단말은 송신만을 할 수 있으며 수신하지 않음.&lt;br /&gt;
: a=inactive : 단말은 송신 및 수신을 할 수 없음 (Hold 버튼을 누를 경우)&lt;br /&gt;
: 별도의 언급이 없을 경우에는 a=sendrecv 로 설정되었다고 가정한다. 미디어의 방향은 다양한 부가 서비스 구현시 유용하다.&lt;br /&gt;
&lt;br /&gt;
* a= (DTMF 협상)&lt;br /&gt;
: DTMF 전달에 관한 협상도 진행한다.&lt;br /&gt;
: a=rtpmap:101 telephone-event.8000 : RFC 2833에 의한 In-band DTMF를 의미.&lt;br /&gt;
: a=fmtp 101 0-15 : DTMF Tone 은 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, *, #, A, B, C, D 총 15 가지를 송수신 함.&lt;br /&gt;
&lt;br /&gt;
== RFC 3264의 기본 SDP 협상 (Offer / Answer Model) ==&lt;br /&gt;
RFC 3264 An Offer/Answer Model Session Description Protocol 권고안에 나와 있는 10.1 Basic Exchange 부분을 살펴보면서 Offer/Answer 모델의 동작 방식을 살펴보자.&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
앨리스는 한개의 음성 스트림과 두 개의 영상 스트림을 제안한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49170 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 51372 RTP/AVP 31&lt;br /&gt;
a=rtpmap:31 H261/90000&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SDP를 Owner이자 Creator인 앨리스는, 음성 스트림은 G.711 ulaw 코덱과 49170 UDP 포트를 사용하며, 영상스트림은 H.261 코덱과 51372 UDP 포트를, 또 하나의 영상 스트림은 MPEG 코덱과 53000 UDP 포트를 사용한다고 제한한다.&lt;br /&gt;
&lt;br /&gt;
=== 밥의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844730 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49920 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 0 RTP/AVP 31&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
밥은 음성 스트림에 대해 G.711 ulaw 코덱과 49920 UDP 포트를 사용하고, 첫번째 영상에 대한 미디어 속성(a=) 정의를 포함하지 않고, 두 번째 영상 스트림에 대해서만 미디어 속성을 포함하고 있다. 일반적으로 미디어 속성(a=)를 포함하지 않으면 미디어 스트림이 개방되지 않는다.&lt;br /&gt;
&lt;br /&gt;
=== 밥의 협상 변경 요청 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
밥은 Answer 후에 협상 내용을 변경을 요청한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844731 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 65422 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 0 RTP/AVP 31&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
m=audio 51434 RTP/AVP 110&lt;br /&gt;
a=rtpmap:110 telephone-events/8000&lt;br /&gt;
a=recvonly&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
밥은 음성 스트림에 대한 UDP 포트 넘버를 49920에서 65422로 변경하는 것과 DTMF 수신을 위한 (receive-only) 방법을 추가하였다. 일반적으로 DTMF 이벤트 처리를 위한 RTP Payload 는 Type 110 이다.&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
앨리스는 밥의 제안을 승인하고, 다음과 같이 응답한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844527 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49170 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 0 RTP/AVP 31&lt;br /&gt;
a=rtpmap:31 H261/90000&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
m=audio 53122 RTP/AVP 110&lt;br /&gt;
a=rtpmap:110 telephone-events/8000&lt;br /&gt;
a=sendonly&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
밥의 요청을 모두 수락한다.&lt;br /&gt;
&lt;br /&gt;
== RFC 3264의 One of N Codec Selection ==&lt;br /&gt;
IP Phone과 Gateway는 음성이나 영상 압축을 위해 DSP (Digital Signal Processor)라는 칩을 사용한다. DSP는 다수의 코덱을 지원하지만, 한 번에 하나의 코덱만을 지원한다. SDP Offer에 다수의 코덱을 전송하더라도 Answer 에서는 하나의 코덱만을 선택할 수 있다. 다수 코덱을 제안할 경우에 선택하는 방식에 대해서 알아보자.&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
Offer이자 Creator인 엘리스는 음성 스트림 하나만을 제안하면서 자신의 DSP가 지원하는 G.711 ulaw, G.723, G.729 코덱을 나역한다. G.711 ulaw 가 가장 우선 순위가 높으며, 코덱 협상 완료 전까지는 미디어를 맏아 들일 수 없으므로 미디어의 방향을 &amp;quot;a=inactive&amp;quot;로 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 62986 RTP/AVP 0 4 18&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=rtpmap:18 G729/8000&lt;br /&gt;
a=inactive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 밥의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
밥은 자신의 DSP가 G.711 ulaw와 G.723 코덱만을 지원하며, G.711 ulaw를 우선적으로 선택한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844731 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 54344 RTP/AVP 0 4&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=inactive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
앨리스는 밥이 제안한 두 개의 코덱 모두를 지원할 수 있지만 G.723 코덱을 선택하면서 &amp;quot;a=sendrecv&amp;quot;로 양방향 통화 채널을 활성화 한다. 일반적으로 우선순위가 높은 G.711 ulaw를 선택하지만, 예제에서는 G.723을 선택하는 것을 가정한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844527 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 62986 RTP/AVP 4&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=sendrecv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 밥의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
밥은 앨리스가 제안한 G.711 코덱을 받아 들이고 &amp;quot;a=sendrecv&amp;quot;로 양방향 통화 채널을 활성화 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844732 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 54344 RTP/AVP 4&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=sendrecv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== inactive to sendrecv ===&lt;br /&gt;
처음 inactive 상태를 sendrecv로 전환하기 위해 4번에 걸친 Offer와 Answer를 교환한다. 일반적으로는 처음부터 a=sendrecv로 교환하고, 처음 제시되는 코덱이 우선순위가 높으므로 선택된다. 위의 과정은 Offer/Answer Model을 기반으로 설명하는 것이지만, 실제로는 INVITE with SDP로 제안이 되며, 200 OK with SDP 또는 180 Ringing with SDP로 2 단계로 마무리된다.&lt;br /&gt;
&lt;br /&gt;
== Early Media in SDP (RFC 3959, RFC 3960) ==&lt;br /&gt;
Early Media 란, 실제 통화가 이루어 지기 전 전송되는 Media 를 의미한다. 대표적인 예로 컬러링이 있다.&lt;br /&gt;
&lt;br /&gt;
=== Early Media 의 개요 ===&lt;br /&gt;
아래 그림은 일반적인 SIP 호 설정 시나리오이다. 앨리스는 수화기를 들어 전화번호를 누르면 INVITE 메시지와 함께 Offer가 이루어진다. 밥의 전화기는 Ringing (따르릉)하게되며, 180 Ringing 메시지가 앨리스에게 전달된다. 180 Ringing을 받은 앨리스는 Local Ringback을 들으면서 밥의 전화기가 울리고 있다고 인지한다. 밥은 Ringing 소리를 듣고 전화가 왔음을 인지하게 되어 전화기로 다가가 수화기를 드는 순간 200 OK 메시지와 Answer가 앨리스에게 전달된다. 200 OK를 받은 앨리스는 ACK를 밥에게 전송하며 통화가 시작된다.&lt;br /&gt;
&lt;br /&gt;
[[File:Sip early media.png]]&lt;br /&gt;
&lt;br /&gt;
그런데 일반적인 SIP 통화 시나리오 상에서 다음 두 가지 문제가 발생하게 된다.&lt;br /&gt;
&lt;br /&gt;
* Remote Ringback&lt;br /&gt;
: 180 Ringing 메시지를 전달받은 앨리스의 전화기는 자체적으로 Ringback tone을 발생시킨다. 그러나, 현실에서는 컬러링이나 Remote Ringback tone을 들을 수 있어야 한다.&lt;br /&gt;
&lt;br /&gt;
* Media Clipping&lt;br /&gt;
: 보통 사람들은 수화기를 들면서 &amp;quot;여보세요&amp;quot;라고 말한다. 위의 시나리오는 200 OK를 보내고 ACK를 받을 때까지의 이야기는 전달할 수 없게 된다. 따라서 Media Clipping이 발생할 수 밖에 없어 밥의 &amp;quot;여보세요&amp;quot;가 앨리스에게는 &amp;quot;세요&amp;quot;라고 들릴 것이다.&lt;br /&gt;
&lt;br /&gt;
이러한 문제점은 SDP의 일반적인 Offer/Answer 절차 이전에 Media가 전송되어져야 한다는 것을 의미한다. 즉, Early Media는 정규 Offer/Answer 절차 이전에 전송되는 RTP를 가리킨다. 그렇다면, Early Media의 발생시점은 INVITE의 생성에서부터 ACK 신호까지이다. ACK 이후에는 정상적인 Media가 전송된다.&lt;br /&gt;
&lt;br /&gt;
=== SDP 협상의 방식 ===&lt;br /&gt;
기본적으로 Early Media가 동작하기 위해서는 위의 그림과 같이 INVITE with SDP로 Initial Offer 가 발생되어야 한다. 위에서 언급한 Media Clipping의 경우, Invite with SDP (offer)로 보내지는 대부분의 경우 이러한 Media Clipping은 발생되지 않는다. UAC(송신자)는 200 OK의 수신 여부와는 상관없이 Offer를 하자마자 자기 스스로 들어오는 Media에 대해 재생할 준비를 하도록 되어 있다. 그러나, 이렇게 Invite with SDP 만 있는 것이 아니다.&lt;br /&gt;
&lt;br /&gt;
아래와 같이 200 OK에 밥이 먼저 Offer를 할 수도 있으며, Update 메소드를 이용하여 SDP를 교환할 수도 있다.&lt;br /&gt;
&lt;br /&gt;
[[File:Sdp late offer.png]]&lt;br /&gt;
&lt;br /&gt;
=== Early Media 의 두 가지 모델 ===&lt;br /&gt;
RFC 3960에 의하면, Early Media는 다음과 같이 두 가지 모델로 구분할 수 있다.&lt;br /&gt;
&lt;br /&gt;
* Gateway Model&lt;br /&gt;
: 이 모델은 SIP 시그널링 상에서 Early Media에 대한 특별한 언급없이 동작한다. 180 Ringing 메시지를 받으면, Local Ringback Tone을 발생시키고, 상대방으로부터 RTP(Early Media)가 전송되면, Local Ringback Tone 발생을 중단하고, RTP를 재생한다. 이 Gateway 모델의 문제점은 Forking 및 Security 문제가 있다.&lt;br /&gt;
&lt;br /&gt;
* Application Server Model&lt;br /&gt;
: SIP 시그널시 Early Media에 대한 Offer/Answer를 가능하게 한다. RFC 3959에 정의된 Content-Disposition 헤더에 Session 또는 Early-session의 새로운 Disposition Type을 설정하여 Early-Media가 가능하게 한다.&lt;br /&gt;
&lt;br /&gt;
=== Ringback Tone 재생 정책 ===&lt;br /&gt;
PSTN 상에서 수신자가 Alert 메시지를 보내면 Ringback Tone은 PBX에 의해 재생된다. SIP의 경우 UAS(수신자)에 의해 Ringback 이 재생되지 않으면, UAC에서 자체적으로 Ringback tone을 재생하기로 되어 있다. 만일, Announcement 또는 Special Ringing Tone이 UAS에 재생이 되면, UAC는 자체적인 재생을 중단하고 UAS로 부터 오는 Media를 재생하는 것이 일반적이다. 그러나, UAS가 Early Media 를 전송하려는 의도 없이 Early Offer를 진행하기도 하고, Reliable Provisional Response 없이 Ealry Media를 전송하기도 하기 때문에 UAC는 쉽게 Local Ringback Tone을 재생해야할 지 말아야할 지를 결정할 수 없다.&lt;br /&gt;
&lt;br /&gt;
Local Ringback Tone 재생에 관련해서 다음과 같은 정책을 구현해야 한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
- 180 Ringing 을 받지 않았다면, Local Ringing 을 재생하지 않는다.&lt;br /&gt;
- 180 Ringing 을 받았으나 수신되는 Media 패킷이 없다면, Local Ringing을 재생한다.&lt;br /&gt;
- 180 Ringing 을 받았으면서 Media 패킷이 수신된다면, Media를 재생하고 Local Ringing을 재생하지 않는다.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
180 Ringing은 수신측이 전화기가 울리고 있음을 의미한다.&lt;br /&gt;
&lt;br /&gt;
=== Application Server Model 상에서의 Early media ===&lt;br /&gt;
Early Media Session 은 지금까지 이야기한 Regual Session에서 함께 처리된다. 그러나, Application Server Model은 Early Media를 위한 별도의 절차를 수행한다. 따라서, 하나의 호 시그널링에서 Early Media Session과 Regular Session 을 동시에 Offer/Answer를 수행한 후 전환한다. 이 때, Content-Disposition Header 에 Session 과 Early-session 이라고 하여 각각의 쓰임새를 구분한다.&lt;br /&gt;
&lt;br /&gt;
Early session 이 Regular session 으로 전환될 때, 코덱을 변경할 필요가 없도록 하기 위하여 Early Media session 과 Media session은 같은 코덱을 사용하는 것을 권장한다. RFC 3959의 예제를 보자.&lt;br /&gt;
&lt;br /&gt;
[[File:Sdp rfc 3959.png]]&lt;br /&gt;
&lt;br /&gt;
앨리스는 밥에게 Contect-Disposition: session 헤더를 INVITE에 추가해서 전송한다. 이 헤더의 의미는 INVITE에 포함된 Offer는 Regular session 에 대한 것임을 표시한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844730 2890844731 IN IP4 host.sip.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.1&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 20000 RTP/AVP 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
이후 밥은 183 Session Progress를 앨리스에게 전송한다. 이때, 다중 메시지가 포함되었음을 의미하는 Content-Type:multipart/mixed 라는 정보를 함께 보낸다. Content-Disposition 헤더를 보면 session과 early-session 두가지가 있는데, 첫번째 바운더리인 Content-Disposition:Session은 Regular session에 대한 것으로 기존의 INVITE with offer에 대한 answer의 내용이다. 두번째 바운더리인 Content-Disposition:early-session은 Early Media를 위한 Offer이다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Content-Type: multipart/mixed; boundary=&amp;quot;boundary1&amp;quot;&lt;br /&gt;
Content-Length: 401&lt;br /&gt;
--boundary1&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=Bob 2890844725 2890844725 IN IP4 host.sip.org&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.2&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 30000 RTP/AVP 0&lt;br /&gt;
&lt;br /&gt;
--boundary1&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: early-session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=Bob 2890844714 2890844714 IN IP4 host.sip.org&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.2&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 30002 RTP/AVP 0&lt;br /&gt;
&lt;br /&gt;
--boundary1--&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
앨리스는 INVITE에 대한 200 OK (수화기를 드는 행동)를 밥으로 받기 전까지 Early-Offer에 대한 Answer를 할 수 있는 방법이 없다. 따라서 이때 PRACK 이 필요하다. 이 메소드를 통해 아래와 같이 Early Offer 에 대한 Answer 를 수행한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: early-session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844717 2890744717 IN IP4 host.sip.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.1&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 20002 RTP/AVP 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
PRACK에 대한 200 OK 를 받게 되면, Early Media를 통해 대화가 가능하다. 이미 Regular session 과 Early session 에 대한 Offer / Answer가 완료되었다.&lt;br /&gt;
&lt;br /&gt;
밥이 수화기를 들어 200 OK 를 전송할 때, Early session 은 Regular session 으로 전환이 이루어진다.&lt;br /&gt;
&lt;br /&gt;
== Delayed(Late) Media ==&lt;br /&gt;
SDP Delayed Offer 는 INVITE 메시지에 SDP OFFER 를 전달하지 않고, 180 RINGING 이나 200 OK 에 SDP OFFER 를 전달하는 방식이다.&lt;br /&gt;
&lt;br /&gt;
SDP ANSWER 는 ACK 와 함께 전달한다. Delayed OFFER 는 수신자가 SDP 협상의 주도권을 가진다. CISCO 제품들은 DELAYED OFFER 를 사용한다.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://www.3cx.com/blog/voip-howto/sdp-voip/&lt;br /&gt;
* http://www.nexpert.net/497&lt;br /&gt;
* http://www.nexpert.net/142&lt;br /&gt;
* https://brunch.co.kr/@linecard/142&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:SIP protocol]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=SIP_SDP&amp;diff=3971</id>
		<title>SIP SDP</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=SIP_SDP&amp;diff=3971"/>
		<updated>2023-09-19T03:32:16Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* See also */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
참조 원문은 이곳&amp;lt;ref&amp;gt;http://www.nexpert.net/497&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;http://www.nexpert.net/142&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
SDP는 코덱 협상과 같은 Capability Exchange 를 수행하는 프로토콜로 SIP 뿐만 아니라 MGCP와 Megaco에서도 사용한다. 기술적으로는 SIP와 SDP 프로토콜을 구분해서 사용하지만, SIP를 살펴볼 때 SDP는 자연스럽게 언급된다.&lt;br /&gt;
&lt;br /&gt;
== SDP 의 개요 ==&lt;br /&gt;
SDP는 Session Description Protocol 의 약어로 멀티미디어 세션 파라미터를 협상하는 프로토콜이다. SDP는 RFC 2327을 개정한 RFC 4566 으로 권고되었다. H.323에 대한 이해가 있다면 SDP가 H.323 프로토콜의 H.245와 비슷한 역할을 수행한다고 생각하면 된다.&lt;br /&gt;
&lt;br /&gt;
SIP를 요청과 응답 모델(Request &amp;amp; Response Model)로 정의하였듯이 SDP는 제안과 수락 모델(Offer &amp;amp; Answer Model)로 정의한다. SDP의 Offer/Answer Model 로의 동작에 대해서는 RFC 33264 An Offer/Answer Model with the SDP 에서 자세히 설명되어 있다.&lt;br /&gt;
&lt;br /&gt;
[[File:Sdp dialog.png]]&lt;br /&gt;
&lt;br /&gt;
SDP는 Capability를 협상하기 위해 기존의 호 처리 프로토콜을 이용한다. 위의 그림에서처럼 SDP는 SIP 메시지와 함께 전달된다. SIP INVITE 메시지에 SDP Offer가 포함되거나 200 OK 응답 메시지에 SDP Answer 가 포함된다.&lt;br /&gt;
&lt;br /&gt;
== SDP 메시지 분석 ==&lt;br /&gt;
SDP는 멀티미디어를 전달하는 RTP 프로토콜에 대한 세부적인 내용을 협상한다. SDP는 아래와 같이 SIP와 다른 메시지 포멧을 유지하지만, SIP와 같이 텍스트 기반으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
=== Session Description ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v= protocal version.&lt;br /&gt;
o= owner/creator and session identification.&lt;br /&gt;
s= session name.&lt;br /&gt;
i= (optional) session information.&lt;br /&gt;
u= (optional) URI of description.&lt;br /&gt;
e= (optional) email address - contact detail.&lt;br /&gt;
p= (optional) phone number - contact detail.&lt;br /&gt;
c= (optional) connection information - not required if included in media description.&lt;br /&gt;
b= (optional) session bandwidth information.&lt;br /&gt;
z= (optional) time zone adjustments.&lt;br /&gt;
k= (optional) encryption key.&lt;br /&gt;
a= (optional) zero or more session attribute lines.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* v=&amp;lt;version&amp;gt;&lt;br /&gt;
: Specifies the version of Session Description Protocol. As specified in RFC 4566, up to now there is only one version, which is Version 0. No minor versions exist.&lt;br /&gt;
&lt;br /&gt;
* o=&amp;lt;username&amp;gt;&amp;lt;sess-id&amp;gt;&amp;lt;sess-version&amp;gt;&amp;lt;nettype&amp;gt;&amp;lt;addrtype&amp;gt;&amp;lt;unicast-address&amp;gt;&lt;br /&gt;
: Details about the originator and identification of the session.&lt;br /&gt;
: &amp;lt;username&amp;gt;: The user's login. The MUST NOT contain spaces.&lt;br /&gt;
: &amp;lt;sess-id&amp;gt;: A numeric string used as unique identifier for the session.&lt;br /&gt;
: &amp;lt;sess-version&amp;gt;: A numeric string used as version number for this session description.&lt;br /&gt;
&lt;br /&gt;
=== Time description ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t= time the session is active.&lt;br /&gt;
r= (optional) repeat times.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Media description&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
m= media name/transport address.&lt;br /&gt;
i= (optional) media title&lt;br /&gt;
c= (optional) connection information - not required if included in session description.&lt;br /&gt;
b= (optional) bandwidth information.&lt;br /&gt;
k= (optional) encryption key.&lt;br /&gt;
a= (optional) zero or more media attribute lines.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844526 IN IP4 atlanta.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 10.1.3.33&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49172 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
각 라인의 의미는 다음과 같다.&lt;br /&gt;
&lt;br /&gt;
* v=0 (필수)&lt;br /&gt;
: SDP 프로토콜의 버전을 표시한다. SDP 버전은 0이다.&lt;br /&gt;
&lt;br /&gt;
* o=alice 2890844526 2890844526 IN IP4 atlanta.com (필수)&lt;br /&gt;
: SDP 메시지를 생성한 Owner/Creator 를 표시한다. 순서대로 Username, Session-ID, Session Version, Network Type, Address Type, Unicast Address 를 표시한다.&lt;br /&gt;
&lt;br /&gt;
* s= (필수)&lt;br /&gt;
: 세션 이름을 표시한다.&lt;br /&gt;
&lt;br /&gt;
* i=(optional)&lt;br /&gt;
: Session information.&lt;br /&gt;
&lt;br /&gt;
* u=(optional)&lt;br /&gt;
: URI of description.&lt;br /&gt;
&lt;br /&gt;
* e=(optional)&lt;br /&gt;
: Email address - contact detail.&lt;br /&gt;
&lt;br /&gt;
* p=(optional)&lt;br /&gt;
: Phone number - contact detail.&lt;br /&gt;
&lt;br /&gt;
* c=IN IP4 10.1.3.33 (optional)&lt;br /&gt;
: 순서대로 Network Type, Address Type, Connection-Address 를 나타내며 미디어의 주소를 정의한다.&lt;br /&gt;
&lt;br /&gt;
* t=0 0 (필수)&lt;br /&gt;
: Timing 으로 Start-time과 End-Time을 표시한다. 0 0 은 고정 세션을 의미한다.&lt;br /&gt;
&lt;br /&gt;
SDP의 Capability Exchange 를 주도하는 라인은 m= 와 a= 로 RTP가 사용할 코덱, IP 주소, 포트넘버가 자세히 명기된다. SDP를 생성하는 UA는 자신이 지원가능한 모든 코덱과 능력을 아래와 같이 명기한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
m=audio 16444 RTP/AVP 0 8 18 101&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
a=ptime:20&lt;br /&gt;
a=rtpmap:8 PCMA/8000&lt;br /&gt;
a=ptime:20&lt;br /&gt;
a=rtpmap:18 G729/8000&lt;br /&gt;
a=ptime:20&lt;br /&gt;
a=sendrecv&lt;br /&gt;
a=rtpmap:101 telephone-event/8000&lt;br /&gt;
a=fmtp:101 0-15 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* m= (미디어 설명)&lt;br /&gt;
: Media Description으로 Media, Port, Protocol, Format 을 정의한다.&lt;br /&gt;
: Media : audio, video, text, application, message 로 표시.&lt;br /&gt;
: Port : 미디어가 전송될 전송포트(Transport port) 표시.&lt;br /&gt;
: Protocol : UDP, RTP/AVP, RTP/SAVP로 표시하며 AVP는 Audio Video Profile의 약자이다.&lt;br /&gt;
: Format : 미디어의 포멧을 서브필드 (a=)로 표시함을 의미.&lt;br /&gt;
: Payload Type 0 8 18 의 순서는 코덱 협상의 우선순위를 나타내며, Payload Type 101은 DTMF 이벤트를 정의한다. 각 포멧에 대한 상세 설명은 a= 에 표시된다.&lt;br /&gt;
&lt;br /&gt;
* a= (미디어 속성)&lt;br /&gt;
: 미디어 속성(attributer)을 정의한다.&lt;br /&gt;
: a=rtpmap : Payload type, encoding name/clock rate 를 표시&lt;br /&gt;
: a=ptime : Packet time으로 미디어 패킷 한 개가 포함된 시간 정보로 ms로 표시 (보통 20ms).&lt;br /&gt;
: a=fmtp : 미디어 포멧에 대한 파라미터를 정의&lt;br /&gt;
&lt;br /&gt;
* a= (미디어의 방향)&lt;br /&gt;
: 미디어 속성 뿐만 아니라 미디어 방향도 표시한다. 미디어의 방향은 아래와 같이 4가지로 나타낸다.&lt;br /&gt;
: a=sendrecv : 단말은 미디어를 송신 및 수신할 수 있음.&lt;br /&gt;
: a=recvonly : 단말은 수신만을 할 수 있으며 송신하지 않음.&lt;br /&gt;
: a=sendonly : 단말은 송신만을 할 수 있으며 수신하지 않음.&lt;br /&gt;
: a=inactive : 단말은 송신 및 수신을 할 수 없음 (Hold 버튼을 누를 경우)&lt;br /&gt;
: 별도의 언급이 없을 경우에는 a=sendrecv 로 설정되었다고 가정한다. 미디어의 방향은 다양한 부가 서비스 구현시 유용하다.&lt;br /&gt;
&lt;br /&gt;
* a= (DTMF 협상)&lt;br /&gt;
: DTMF 전달에 관한 협상도 진행한다.&lt;br /&gt;
: a=rtpmap:101 telephone-event.8000 : RFC 2833에 의한 In-band DTMF를 의미.&lt;br /&gt;
: a=fmtp 101 0-15 : DTMF Tone 은 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, *, #, A, B, C, D 총 15 가지를 송수신 함.&lt;br /&gt;
&lt;br /&gt;
== RFC 3264의 기본 SDP 협상 (Offer / Answer Model) ==&lt;br /&gt;
RFC 3264 An Offer/Answer Model Session Description Protocol 권고안에 나와 있는 10.1 Basic Exchange 부분을 살펴보면서 Offer/Answer 모델의 동작 방식을 살펴보자.&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
앨리스는 한개의 음성 스트림과 두 개의 영상 스트림을 제안한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49170 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 51372 RTP/AVP 31&lt;br /&gt;
a=rtpmap:31 H261/90000&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SDP를 Owner이자 Creator인 앨리스는, 음성 스트림은 G.711 ulaw 코덱과 49170 UDP 포트를 사용하며, 영상스트림은 H.261 코덱과 51372 UDP 포트를, 또 하나의 영상 스트림은 MPEG 코덱과 53000 UDP 포트를 사용한다고 제한한다.&lt;br /&gt;
&lt;br /&gt;
=== 밥의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844730 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49920 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 0 RTP/AVP 31&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
밥은 음성 스트림에 대해 G.711 ulaw 코덱과 49920 UDP 포트를 사용하고, 첫번째 영상에 대한 미디어 속성(a=) 정의를 포함하지 않고, 두 번째 영상 스트림에 대해서만 미디어 속성을 포함하고 있다. 일반적으로 미디어 속성(a=)를 포함하지 않으면 미디어 스트림이 개방되지 않는다.&lt;br /&gt;
&lt;br /&gt;
=== 밥의 협상 변경 요청 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
밥은 Answer 후에 협상 내용을 변경을 요청한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844731 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 65422 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 0 RTP/AVP 31&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
m=audio 51434 RTP/AVP 110&lt;br /&gt;
a=rtpmap:110 telephone-events/8000&lt;br /&gt;
a=recvonly&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
밥은 음성 스트림에 대한 UDP 포트 넘버를 49920에서 65422로 변경하는 것과 DTMF 수신을 위한 (receive-only) 방법을 추가하였다. 일반적으로 DTMF 이벤트 처리를 위한 RTP Payload 는 Type 110 이다.&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
앨리스는 밥의 제안을 승인하고, 다음과 같이 응답한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844527 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49170 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 0 RTP/AVP 31&lt;br /&gt;
a=rtpmap:31 H261/90000&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
m=audio 53122 RTP/AVP 110&lt;br /&gt;
a=rtpmap:110 telephone-events/8000&lt;br /&gt;
a=sendonly&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
밥의 요청을 모두 수락한다.&lt;br /&gt;
&lt;br /&gt;
== RFC 3264의 One of N Codec Selection ==&lt;br /&gt;
IP Phone과 Gateway는 음성이나 영상 압축을 위해 DSP (Digital Signal Processor)라는 칩을 사용한다. DSP는 다수의 코덱을 지원하지만, 한 번에 하나의 코덱만을 지원한다. SDP Offer에 다수의 코덱을 전송하더라도 Answer 에서는 하나의 코덱만을 선택할 수 있다. 다수 코덱을 제안할 경우에 선택하는 방식에 대해서 알아보자.&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
Offer이자 Creator인 엘리스는 음성 스트림 하나만을 제안하면서 자신의 DSP가 지원하는 G.711 ulaw, G.723, G.729 코덱을 나역한다. G.711 ulaw 가 가장 우선 순위가 높으며, 코덱 협상 완료 전까지는 미디어를 맏아 들일 수 없으므로 미디어의 방향을 &amp;quot;a=inactive&amp;quot;로 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 62986 RTP/AVP 0 4 18&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=rtpmap:18 G729/8000&lt;br /&gt;
a=inactive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 밥의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
밥은 자신의 DSP가 G.711 ulaw와 G.723 코덱만을 지원하며, G.711 ulaw를 우선적으로 선택한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844731 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 54344 RTP/AVP 0 4&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=inactive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
앨리스는 밥이 제안한 두 개의 코덱 모두를 지원할 수 있지만 G.723 코덱을 선택하면서 &amp;quot;a=sendrecv&amp;quot;로 양방향 통화 채널을 활성화 한다. 일반적으로 우선순위가 높은 G.711 ulaw를 선택하지만, 예제에서는 G.723을 선택하는 것을 가정한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844527 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 62986 RTP/AVP 4&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=sendrecv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 밥의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
밥은 앨리스가 제안한 G.711 코덱을 받아 들이고 &amp;quot;a=sendrecv&amp;quot;로 양방향 통화 채널을 활성화 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844732 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 54344 RTP/AVP 4&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=sendrecv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== inactive to sendrecv ===&lt;br /&gt;
처음 inactive 상태를 sendrecv로 전환하기 위해 4번에 걸친 Offer와 Answer를 교환한다. 일반적으로는 처음부터 a=sendrecv로 교환하고, 처음 제시되는 코덱이 우선순위가 높으므로 선택된다. 위의 과정은 Offer/Answer Model을 기반으로 설명하는 것이지만, 실제로는 INVITE with SDP로 제안이 되며, 200 OK with SDP 또는 180 Ringing with SDP로 2 단계로 마무리된다.&lt;br /&gt;
&lt;br /&gt;
== Early Media in SDP (RFC 3959, RFC 3960) ==&lt;br /&gt;
Early Media 란, 실제 통화가 이루어 지기 전 전송되는 Media 를 의미한다. 대표적인 예로 컬러링이 있다.&lt;br /&gt;
&lt;br /&gt;
=== Early Media 의 개요 ===&lt;br /&gt;
아래 그림은 일반적인 SIP 호 설정 시나리오이다. 앨리스는 수화기를 들어 전화번호를 누르면 INVITE 메시지와 함께 Offer가 이루어진다. 밥의 전화기는 Ringing (따르릉)하게되며, 180 Ringing 메시지가 앨리스에게 전달된다. 180 Ringing을 받은 앨리스는 Local Ringback을 들으면서 밥의 전화기가 울리고 있다고 인지한다. 밥은 Ringing 소리를 듣고 전화가 왔음을 인지하게 되어 전화기로 다가가 수화기를 드는 순간 200 OK 메시지와 Answer가 앨리스에게 전달된다. 200 OK를 받은 앨리스는 ACK를 밥에게 전송하며 통화가 시작된다.&lt;br /&gt;
&lt;br /&gt;
[[File:Sip early media.png]]&lt;br /&gt;
&lt;br /&gt;
그런데 일반적인 SIP 통화 시나리오 상에서 다음 두 가지 문제가 발생하게 된다.&lt;br /&gt;
&lt;br /&gt;
* Remote Ringback&lt;br /&gt;
: 180 Ringing 메시지를 전달받은 앨리스의 전화기는 자체적으로 Ringback tone을 발생시킨다. 그러나, 현실에서는 컬러링이나 Remote Ringback tone을 들을 수 있어야 한다.&lt;br /&gt;
&lt;br /&gt;
* Media Clipping&lt;br /&gt;
: 보통 사람들은 수화기를 들면서 &amp;quot;여보세요&amp;quot;라고 말한다. 위의 시나리오는 200 OK를 보내고 ACK를 받을 때까지의 이야기는 전달할 수 없게 된다. 따라서 Media Clipping이 발생할 수 밖에 없어 밥의 &amp;quot;여보세요&amp;quot;가 앨리스에게는 &amp;quot;세요&amp;quot;라고 들릴 것이다.&lt;br /&gt;
&lt;br /&gt;
이러한 문제점은 SDP의 일반적인 Offer/Answer 절차 이전에 Media가 전송되어져야 한다는 것을 의미한다. 즉, Early Media는 정규 Offer/Answer 절차 이전에 전송되는 RTP를 가리킨다. 그렇다면, Early Media의 발생시점은 INVITE의 생성에서부터 ACK 신호까지이다. ACK 이후에는 정상적인 Media가 전송된다.&lt;br /&gt;
&lt;br /&gt;
=== SDP 협상의 방식 ===&lt;br /&gt;
기본적으로 Early Media가 동작하기 위해서는 위의 그림과 같이 INVITE with SDP로 Initial Offer 가 발생되어야 한다. 위에서 언급한 Media Clipping의 경우, Invite with SDP (offer)로 보내지는 대부분의 경우 이러한 Media Clipping은 발생되지 않는다. UAC(송신자)는 200 OK의 수신 여부와는 상관없이 Offer를 하자마자 자기 스스로 들어오는 Media에 대해 재생할 준비를 하도록 되어 있다. 그러나, 이렇게 Invite with SDP 만 있는 것이 아니다.&lt;br /&gt;
&lt;br /&gt;
아래와 같이 200 OK에 밥이 먼저 Offer를 할 수도 있으며, Update 메소드를 이용하여 SDP를 교환할 수도 있다.&lt;br /&gt;
&lt;br /&gt;
[[File:Sdp late offer.png]]&lt;br /&gt;
&lt;br /&gt;
=== Early Media 의 두 가지 모델 ===&lt;br /&gt;
RFC 3960에 의하면, Early Media는 다음과 같이 두 가지 모델로 구분할 수 있다.&lt;br /&gt;
&lt;br /&gt;
* Gateway Model&lt;br /&gt;
: 이 모델은 SIP 시그널링 상에서 Early Media에 대한 특별한 언급없이 동작한다. 180 Ringing 메시지를 받으면, Local Ringback Tone을 발생시키고, 상대방으로부터 RTP(Early Media)가 전송되면, Local Ringback Tone 발생을 중단하고, RTP를 재생한다. 이 Gateway 모델의 문제점은 Forking 및 Security 문제가 있다.&lt;br /&gt;
&lt;br /&gt;
* Application Server Model&lt;br /&gt;
: SIP 시그널시 Early Media에 대한 Offer/Answer를 가능하게 한다. RFC 3959에 정의된 Content-Disposition 헤더에 Session 또는 Early-session의 새로운 Disposition Type을 설정하여 Early-Media가 가능하게 한다.&lt;br /&gt;
&lt;br /&gt;
=== Ringback Tone 재생 정책 ===&lt;br /&gt;
PSTN 상에서 수신자가 Alert 메시지를 보내면 Ringback Tone은 PBX에 의해 재생된다. SIP의 경우 UAS(수신자)에 의해 Ringback 이 재생되지 않으면, UAC에서 자체적으로 Ringback tone을 재생하기로 되어 있다. 만일, Announcement 또는 Special Ringing Tone이 UAS에 재생이 되면, UAC는 자체적인 재생을 중단하고 UAS로 부터 오는 Media를 재생하는 것이 일반적이다. 그러나, UAS가 Early Media 를 전송하려는 의도 없이 Early Offer를 진행하기도 하고, Reliable Provisional Response 없이 Ealry Media를 전송하기도 하기 때문에 UAC는 쉽게 Local Ringback Tone을 재생해야할 지 말아야할 지를 결정할 수 없다.&lt;br /&gt;
&lt;br /&gt;
Local Ringback Tone 재생에 관련해서 다음과 같은 정책을 구현해야 한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
- 180 Ringing 을 받지 않았다면, Local Ringing 을 재생하지 않는다.&lt;br /&gt;
- 180 Ringing 을 받았으나 수신되는 Media 패킷이 없다면, Local Ringing을 재생한다.&lt;br /&gt;
- 180 Ringing 을 받았으면서 Media 패킷이 수신된다면, Media를 재생하고 Local Ringing을 재생하지 않는다.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
180 Ringing은 수신측이 전화기가 울리고 있음을 의미한다.&lt;br /&gt;
&lt;br /&gt;
=== Application Server Model 상에서의 Early media ===&lt;br /&gt;
Early Media Session 은 지금까지 이야기한 Regual Session에서 함께 처리된다. 그러나, Application Server Model은 Early Media를 위한 별도의 절차를 수행한다. 따라서, 하나의 호 시그널링에서 Early Media Session과 Regular Session 을 동시에 Offer/Answer를 수행한 후 전환한다. 이 때, Content-Disposition Header 에 Session 과 Early-session 이라고 하여 각각의 쓰임새를 구분한다.&lt;br /&gt;
&lt;br /&gt;
Early session 이 Regular session 으로 전환될 때, 코덱을 변경할 필요가 없도록 하기 위하여 Early Media session 과 Media session은 같은 코덱을 사용하는 것을 권장한다. RFC 3959의 예제를 보자.&lt;br /&gt;
&lt;br /&gt;
[[File:Sdp rfc 3959.png]]&lt;br /&gt;
&lt;br /&gt;
앨리스는 밥에게 Contect-Disposition: session 헤더를 INVITE에 추가해서 전송한다. 이 헤더의 의미는 INVITE에 포함된 Offer는 Regular session 에 대한 것임을 표시한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844730 2890844731 IN IP4 host.sip.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.1&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 20000 RTP/AVP 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
이후 밥은 183 Session Progress를 앨리스에게 전송한다. 이때, 다중 메시지가 포함되었음을 의미하는 Content-Type:multipart/mixed 라는 정보를 함께 보낸다. Content-Disposition 헤더를 보면 session과 early-session 두가지가 있는데, 첫번째 바운더리인 Content-Disposition:Session은 Regular session에 대한 것으로 기존의 INVITE with offer에 대한 answer의 내용이다. 두번째 바운더리인 Content-Disposition:early-session은 Early Media를 위한 Offer이다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Content-Type: multipart/mixed; boundary=&amp;quot;boundary1&amp;quot;&lt;br /&gt;
Content-Length: 401&lt;br /&gt;
--boundary1&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=Bob 2890844725 2890844725 IN IP4 host.sip.org&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.2&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 30000 RTP/AVP 0&lt;br /&gt;
&lt;br /&gt;
--boundary1&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: early-session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=Bob 2890844714 2890844714 IN IP4 host.sip.org&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.2&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 30002 RTP/AVP 0&lt;br /&gt;
&lt;br /&gt;
--boundary1--&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
앨리스는 INVITE에 대한 200 OK (수화기를 드는 행동)를 밥으로 받기 전까지 Early-Offer에 대한 Answer를 할 수 있는 방법이 없다. 따라서 이때 PRACK 이 필요하다. 이 메소드를 통해 아래와 같이 Early Offer 에 대한 Answer 를 수행한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: early-session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844717 2890744717 IN IP4 host.sip.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.1&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 20002 RTP/AVP 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
PRACK에 대한 200 OK 를 받게 되면, Early Media를 통해 대화가 가능하다. 이미 Regular session 과 Early session 에 대한 Offer / Answer가 완료되었다.&lt;br /&gt;
&lt;br /&gt;
밥이 수화기를 들어 200 OK 를 전송할 때, Early session 은 Regular session 으로 전환이 이루어진다.&lt;br /&gt;
&lt;br /&gt;
== Delayed(Late) Media in SDP (RFC 3959, RFC 3960) ==&lt;br /&gt;
SDP Delayed Offer 는 INVITE 메시지에 SDP OFFER 를 전달하지 않고, 180 RINGING 이나 200 OK 에 SDP OFFER 를 전달하는 방식이다.&lt;br /&gt;
&lt;br /&gt;
SDP ANSWER 는 ACK 와 함께 전달한다. Delayed OFFER 는 수신자가 SDP 협상의 주도권을 가진다. CISCO 제품들은 DELAYED OFFER 를 사용한다.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://www.3cx.com/blog/voip-howto/sdp-voip/&lt;br /&gt;
* http://www.nexpert.net/497&lt;br /&gt;
* http://www.nexpert.net/142&lt;br /&gt;
* https://brunch.co.kr/@linecard/142&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:SIP protocol]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=SIP_SDP&amp;diff=3970</id>
		<title>SIP SDP</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=SIP_SDP&amp;diff=3970"/>
		<updated>2023-09-19T03:31:41Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Early Media in SDP (RFC 3959, RFC 3960) */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
참조 원문은 이곳&amp;lt;ref&amp;gt;http://www.nexpert.net/497&amp;lt;/ref&amp;gt;&amp;lt;ref&amp;gt;http://www.nexpert.net/142&amp;lt;/ref&amp;gt;에서 확인할 수 있다.&lt;br /&gt;
&lt;br /&gt;
SDP는 코덱 협상과 같은 Capability Exchange 를 수행하는 프로토콜로 SIP 뿐만 아니라 MGCP와 Megaco에서도 사용한다. 기술적으로는 SIP와 SDP 프로토콜을 구분해서 사용하지만, SIP를 살펴볼 때 SDP는 자연스럽게 언급된다.&lt;br /&gt;
&lt;br /&gt;
== SDP 의 개요 ==&lt;br /&gt;
SDP는 Session Description Protocol 의 약어로 멀티미디어 세션 파라미터를 협상하는 프로토콜이다. SDP는 RFC 2327을 개정한 RFC 4566 으로 권고되었다. H.323에 대한 이해가 있다면 SDP가 H.323 프로토콜의 H.245와 비슷한 역할을 수행한다고 생각하면 된다.&lt;br /&gt;
&lt;br /&gt;
SIP를 요청과 응답 모델(Request &amp;amp; Response Model)로 정의하였듯이 SDP는 제안과 수락 모델(Offer &amp;amp; Answer Model)로 정의한다. SDP의 Offer/Answer Model 로의 동작에 대해서는 RFC 33264 An Offer/Answer Model with the SDP 에서 자세히 설명되어 있다.&lt;br /&gt;
&lt;br /&gt;
[[File:Sdp dialog.png]]&lt;br /&gt;
&lt;br /&gt;
SDP는 Capability를 협상하기 위해 기존의 호 처리 프로토콜을 이용한다. 위의 그림에서처럼 SDP는 SIP 메시지와 함께 전달된다. SIP INVITE 메시지에 SDP Offer가 포함되거나 200 OK 응답 메시지에 SDP Answer 가 포함된다.&lt;br /&gt;
&lt;br /&gt;
== SDP 메시지 분석 ==&lt;br /&gt;
SDP는 멀티미디어를 전달하는 RTP 프로토콜에 대한 세부적인 내용을 협상한다. SDP는 아래와 같이 SIP와 다른 메시지 포멧을 유지하지만, SIP와 같이 텍스트 기반으로 구성된다.&lt;br /&gt;
&lt;br /&gt;
=== Session Description ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v= protocal version.&lt;br /&gt;
o= owner/creator and session identification.&lt;br /&gt;
s= session name.&lt;br /&gt;
i= (optional) session information.&lt;br /&gt;
u= (optional) URI of description.&lt;br /&gt;
e= (optional) email address - contact detail.&lt;br /&gt;
p= (optional) phone number - contact detail.&lt;br /&gt;
c= (optional) connection information - not required if included in media description.&lt;br /&gt;
b= (optional) session bandwidth information.&lt;br /&gt;
z= (optional) time zone adjustments.&lt;br /&gt;
k= (optional) encryption key.&lt;br /&gt;
a= (optional) zero or more session attribute lines.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* v=&amp;lt;version&amp;gt;&lt;br /&gt;
: Specifies the version of Session Description Protocol. As specified in RFC 4566, up to now there is only one version, which is Version 0. No minor versions exist.&lt;br /&gt;
&lt;br /&gt;
* o=&amp;lt;username&amp;gt;&amp;lt;sess-id&amp;gt;&amp;lt;sess-version&amp;gt;&amp;lt;nettype&amp;gt;&amp;lt;addrtype&amp;gt;&amp;lt;unicast-address&amp;gt;&lt;br /&gt;
: Details about the originator and identification of the session.&lt;br /&gt;
: &amp;lt;username&amp;gt;: The user's login. The MUST NOT contain spaces.&lt;br /&gt;
: &amp;lt;sess-id&amp;gt;: A numeric string used as unique identifier for the session.&lt;br /&gt;
: &amp;lt;sess-version&amp;gt;: A numeric string used as version number for this session description.&lt;br /&gt;
&lt;br /&gt;
=== Time description ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
t= time the session is active.&lt;br /&gt;
r= (optional) repeat times.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Media description&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
m= media name/transport address.&lt;br /&gt;
i= (optional) media title&lt;br /&gt;
c= (optional) connection information - not required if included in session description.&lt;br /&gt;
b= (optional) bandwidth information.&lt;br /&gt;
k= (optional) encryption key.&lt;br /&gt;
a= (optional) zero or more media attribute lines.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844526 IN IP4 atlanta.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 10.1.3.33&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49172 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
각 라인의 의미는 다음과 같다.&lt;br /&gt;
&lt;br /&gt;
* v=0 (필수)&lt;br /&gt;
: SDP 프로토콜의 버전을 표시한다. SDP 버전은 0이다.&lt;br /&gt;
&lt;br /&gt;
* o=alice 2890844526 2890844526 IN IP4 atlanta.com (필수)&lt;br /&gt;
: SDP 메시지를 생성한 Owner/Creator 를 표시한다. 순서대로 Username, Session-ID, Session Version, Network Type, Address Type, Unicast Address 를 표시한다.&lt;br /&gt;
&lt;br /&gt;
* s= (필수)&lt;br /&gt;
: 세션 이름을 표시한다.&lt;br /&gt;
&lt;br /&gt;
* i=(optional)&lt;br /&gt;
: Session information.&lt;br /&gt;
&lt;br /&gt;
* u=(optional)&lt;br /&gt;
: URI of description.&lt;br /&gt;
&lt;br /&gt;
* e=(optional)&lt;br /&gt;
: Email address - contact detail.&lt;br /&gt;
&lt;br /&gt;
* p=(optional)&lt;br /&gt;
: Phone number - contact detail.&lt;br /&gt;
&lt;br /&gt;
* c=IN IP4 10.1.3.33 (optional)&lt;br /&gt;
: 순서대로 Network Type, Address Type, Connection-Address 를 나타내며 미디어의 주소를 정의한다.&lt;br /&gt;
&lt;br /&gt;
* t=0 0 (필수)&lt;br /&gt;
: Timing 으로 Start-time과 End-Time을 표시한다. 0 0 은 고정 세션을 의미한다.&lt;br /&gt;
&lt;br /&gt;
SDP의 Capability Exchange 를 주도하는 라인은 m= 와 a= 로 RTP가 사용할 코덱, IP 주소, 포트넘버가 자세히 명기된다. SDP를 생성하는 UA는 자신이 지원가능한 모든 코덱과 능력을 아래와 같이 명기한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
m=audio 16444 RTP/AVP 0 8 18 101&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
a=ptime:20&lt;br /&gt;
a=rtpmap:8 PCMA/8000&lt;br /&gt;
a=ptime:20&lt;br /&gt;
a=rtpmap:18 G729/8000&lt;br /&gt;
a=ptime:20&lt;br /&gt;
a=sendrecv&lt;br /&gt;
a=rtpmap:101 telephone-event/8000&lt;br /&gt;
a=fmtp:101 0-15 &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* m= (미디어 설명)&lt;br /&gt;
: Media Description으로 Media, Port, Protocol, Format 을 정의한다.&lt;br /&gt;
: Media : audio, video, text, application, message 로 표시.&lt;br /&gt;
: Port : 미디어가 전송될 전송포트(Transport port) 표시.&lt;br /&gt;
: Protocol : UDP, RTP/AVP, RTP/SAVP로 표시하며 AVP는 Audio Video Profile의 약자이다.&lt;br /&gt;
: Format : 미디어의 포멧을 서브필드 (a=)로 표시함을 의미.&lt;br /&gt;
: Payload Type 0 8 18 의 순서는 코덱 협상의 우선순위를 나타내며, Payload Type 101은 DTMF 이벤트를 정의한다. 각 포멧에 대한 상세 설명은 a= 에 표시된다.&lt;br /&gt;
&lt;br /&gt;
* a= (미디어 속성)&lt;br /&gt;
: 미디어 속성(attributer)을 정의한다.&lt;br /&gt;
: a=rtpmap : Payload type, encoding name/clock rate 를 표시&lt;br /&gt;
: a=ptime : Packet time으로 미디어 패킷 한 개가 포함된 시간 정보로 ms로 표시 (보통 20ms).&lt;br /&gt;
: a=fmtp : 미디어 포멧에 대한 파라미터를 정의&lt;br /&gt;
&lt;br /&gt;
* a= (미디어의 방향)&lt;br /&gt;
: 미디어 속성 뿐만 아니라 미디어 방향도 표시한다. 미디어의 방향은 아래와 같이 4가지로 나타낸다.&lt;br /&gt;
: a=sendrecv : 단말은 미디어를 송신 및 수신할 수 있음.&lt;br /&gt;
: a=recvonly : 단말은 수신만을 할 수 있으며 송신하지 않음.&lt;br /&gt;
: a=sendonly : 단말은 송신만을 할 수 있으며 수신하지 않음.&lt;br /&gt;
: a=inactive : 단말은 송신 및 수신을 할 수 없음 (Hold 버튼을 누를 경우)&lt;br /&gt;
: 별도의 언급이 없을 경우에는 a=sendrecv 로 설정되었다고 가정한다. 미디어의 방향은 다양한 부가 서비스 구현시 유용하다.&lt;br /&gt;
&lt;br /&gt;
* a= (DTMF 협상)&lt;br /&gt;
: DTMF 전달에 관한 협상도 진행한다.&lt;br /&gt;
: a=rtpmap:101 telephone-event.8000 : RFC 2833에 의한 In-band DTMF를 의미.&lt;br /&gt;
: a=fmtp 101 0-15 : DTMF Tone 은 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, *, #, A, B, C, D 총 15 가지를 송수신 함.&lt;br /&gt;
&lt;br /&gt;
== RFC 3264의 기본 SDP 협상 (Offer / Answer Model) ==&lt;br /&gt;
RFC 3264 An Offer/Answer Model Session Description Protocol 권고안에 나와 있는 10.1 Basic Exchange 부분을 살펴보면서 Offer/Answer 모델의 동작 방식을 살펴보자.&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
앨리스는 한개의 음성 스트림과 두 개의 영상 스트림을 제안한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49170 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 51372 RTP/AVP 31&lt;br /&gt;
a=rtpmap:31 H261/90000&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
SDP를 Owner이자 Creator인 앨리스는, 음성 스트림은 G.711 ulaw 코덱과 49170 UDP 포트를 사용하며, 영상스트림은 H.261 코덱과 51372 UDP 포트를, 또 하나의 영상 스트림은 MPEG 코덱과 53000 UDP 포트를 사용한다고 제한한다.&lt;br /&gt;
&lt;br /&gt;
=== 밥의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844730 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49920 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 0 RTP/AVP 31&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
밥은 음성 스트림에 대해 G.711 ulaw 코덱과 49920 UDP 포트를 사용하고, 첫번째 영상에 대한 미디어 속성(a=) 정의를 포함하지 않고, 두 번째 영상 스트림에 대해서만 미디어 속성을 포함하고 있다. 일반적으로 미디어 속성(a=)를 포함하지 않으면 미디어 스트림이 개방되지 않는다.&lt;br /&gt;
&lt;br /&gt;
=== 밥의 협상 변경 요청 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
밥은 Answer 후에 협상 내용을 변경을 요청한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844731 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 65422 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 0 RTP/AVP 31&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
m=audio 51434 RTP/AVP 110&lt;br /&gt;
a=rtpmap:110 telephone-events/8000&lt;br /&gt;
a=recvonly&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
밥은 음성 스트림에 대한 UDP 포트 넘버를 49920에서 65422로 변경하는 것과 DTMF 수신을 위한 (receive-only) 방법을 추가하였다. 일반적으로 DTMF 이벤트 처리를 위한 RTP Payload 는 Type 110 이다.&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
앨리스는 밥의 제안을 승인하고, 다음과 같이 응답한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844527 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 49170 RTP/AVP 0&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
m=video 0 RTP/AVP 31&lt;br /&gt;
a=rtpmap:31 H261/90000&lt;br /&gt;
m=video 53000 RTP/AVP 32&lt;br /&gt;
a=rtpmap:32 MPV/90000&lt;br /&gt;
m=audio 53122 RTP/AVP 110&lt;br /&gt;
a=rtpmap:110 telephone-events/8000&lt;br /&gt;
a=sendonly&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
밥의 요청을 모두 수락한다.&lt;br /&gt;
&lt;br /&gt;
== RFC 3264의 One of N Codec Selection ==&lt;br /&gt;
IP Phone과 Gateway는 음성이나 영상 압축을 위해 DSP (Digital Signal Processor)라는 칩을 사용한다. DSP는 다수의 코덱을 지원하지만, 한 번에 하나의 코덱만을 지원한다. SDP Offer에 다수의 코덱을 전송하더라도 Answer 에서는 하나의 코덱만을 선택할 수 있다. 다수 코덱을 제안할 경우에 선택하는 방식에 대해서 알아보자.&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
Offer이자 Creator인 엘리스는 음성 스트림 하나만을 제안하면서 자신의 DSP가 지원하는 G.711 ulaw, G.723, G.729 코덱을 나역한다. G.711 ulaw 가 가장 우선 순위가 높으며, 코덱 협상 완료 전까지는 미디어를 맏아 들일 수 없으므로 미디어의 방향을 &amp;quot;a=inactive&amp;quot;로 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844526 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 62986 RTP/AVP 0 4 18&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=rtpmap:18 G729/8000&lt;br /&gt;
a=inactive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 밥의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
밥은 자신의 DSP가 G.711 ulaw와 G.723 코덱만을 지원하며, G.711 ulaw를 우선적으로 선택한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844731 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 54344 RTP/AVP 0 4&lt;br /&gt;
a=rtpmap:0 PCMU/8000&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=inactive&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 앨리스의 &amp;quot;Offer&amp;quot; ===&lt;br /&gt;
앨리스는 밥이 제안한 두 개의 코덱 모두를 지원할 수 있지만 G.723 코덱을 선택하면서 &amp;quot;a=sendrecv&amp;quot;로 양방향 통화 채널을 활성화 한다. 일반적으로 우선순위가 높은 G.711 ulaw를 선택하지만, 예제에서는 G.723을 선택하는 것을 가정한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844526 2890844527 IN IP4 host.anywhere.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.anywhere.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 62986 RTP/AVP 4&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=sendrecv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== 밥의 &amp;quot;Answer&amp;quot; ===&lt;br /&gt;
밥은 앨리스가 제안한 G.711 코덱을 받아 들이고 &amp;quot;a=sendrecv&amp;quot;로 양방향 통화 채널을 활성화 한다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
v=0&lt;br /&gt;
o=bob 2890844730 2890844732 IN IP4 host.example.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 host.example.com&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 54344 RTP/AVP 4&lt;br /&gt;
a=rtpmap:4 G723/8000&lt;br /&gt;
a=sendrecv&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== inactive to sendrecv ===&lt;br /&gt;
처음 inactive 상태를 sendrecv로 전환하기 위해 4번에 걸친 Offer와 Answer를 교환한다. 일반적으로는 처음부터 a=sendrecv로 교환하고, 처음 제시되는 코덱이 우선순위가 높으므로 선택된다. 위의 과정은 Offer/Answer Model을 기반으로 설명하는 것이지만, 실제로는 INVITE with SDP로 제안이 되며, 200 OK with SDP 또는 180 Ringing with SDP로 2 단계로 마무리된다.&lt;br /&gt;
&lt;br /&gt;
== Early Media in SDP (RFC 3959, RFC 3960) ==&lt;br /&gt;
Early Media 란, 실제 통화가 이루어 지기 전 전송되는 Media 를 의미한다. 대표적인 예로 컬러링이 있다.&lt;br /&gt;
&lt;br /&gt;
=== Early Media 의 개요 ===&lt;br /&gt;
아래 그림은 일반적인 SIP 호 설정 시나리오이다. 앨리스는 수화기를 들어 전화번호를 누르면 INVITE 메시지와 함께 Offer가 이루어진다. 밥의 전화기는 Ringing (따르릉)하게되며, 180 Ringing 메시지가 앨리스에게 전달된다. 180 Ringing을 받은 앨리스는 Local Ringback을 들으면서 밥의 전화기가 울리고 있다고 인지한다. 밥은 Ringing 소리를 듣고 전화가 왔음을 인지하게 되어 전화기로 다가가 수화기를 드는 순간 200 OK 메시지와 Answer가 앨리스에게 전달된다. 200 OK를 받은 앨리스는 ACK를 밥에게 전송하며 통화가 시작된다.&lt;br /&gt;
&lt;br /&gt;
[[File:Sip early media.png]]&lt;br /&gt;
&lt;br /&gt;
그런데 일반적인 SIP 통화 시나리오 상에서 다음 두 가지 문제가 발생하게 된다.&lt;br /&gt;
&lt;br /&gt;
* Remote Ringback&lt;br /&gt;
: 180 Ringing 메시지를 전달받은 앨리스의 전화기는 자체적으로 Ringback tone을 발생시킨다. 그러나, 현실에서는 컬러링이나 Remote Ringback tone을 들을 수 있어야 한다.&lt;br /&gt;
&lt;br /&gt;
* Media Clipping&lt;br /&gt;
: 보통 사람들은 수화기를 들면서 &amp;quot;여보세요&amp;quot;라고 말한다. 위의 시나리오는 200 OK를 보내고 ACK를 받을 때까지의 이야기는 전달할 수 없게 된다. 따라서 Media Clipping이 발생할 수 밖에 없어 밥의 &amp;quot;여보세요&amp;quot;가 앨리스에게는 &amp;quot;세요&amp;quot;라고 들릴 것이다.&lt;br /&gt;
&lt;br /&gt;
이러한 문제점은 SDP의 일반적인 Offer/Answer 절차 이전에 Media가 전송되어져야 한다는 것을 의미한다. 즉, Early Media는 정규 Offer/Answer 절차 이전에 전송되는 RTP를 가리킨다. 그렇다면, Early Media의 발생시점은 INVITE의 생성에서부터 ACK 신호까지이다. ACK 이후에는 정상적인 Media가 전송된다.&lt;br /&gt;
&lt;br /&gt;
=== SDP 협상의 방식 ===&lt;br /&gt;
기본적으로 Early Media가 동작하기 위해서는 위의 그림과 같이 INVITE with SDP로 Initial Offer 가 발생되어야 한다. 위에서 언급한 Media Clipping의 경우, Invite with SDP (offer)로 보내지는 대부분의 경우 이러한 Media Clipping은 발생되지 않는다. UAC(송신자)는 200 OK의 수신 여부와는 상관없이 Offer를 하자마자 자기 스스로 들어오는 Media에 대해 재생할 준비를 하도록 되어 있다. 그러나, 이렇게 Invite with SDP 만 있는 것이 아니다.&lt;br /&gt;
&lt;br /&gt;
아래와 같이 200 OK에 밥이 먼저 Offer를 할 수도 있으며, Update 메소드를 이용하여 SDP를 교환할 수도 있다.&lt;br /&gt;
&lt;br /&gt;
[[File:Sdp late offer.png]]&lt;br /&gt;
&lt;br /&gt;
=== Early Media 의 두 가지 모델 ===&lt;br /&gt;
RFC 3960에 의하면, Early Media는 다음과 같이 두 가지 모델로 구분할 수 있다.&lt;br /&gt;
&lt;br /&gt;
* Gateway Model&lt;br /&gt;
: 이 모델은 SIP 시그널링 상에서 Early Media에 대한 특별한 언급없이 동작한다. 180 Ringing 메시지를 받으면, Local Ringback Tone을 발생시키고, 상대방으로부터 RTP(Early Media)가 전송되면, Local Ringback Tone 발생을 중단하고, RTP를 재생한다. 이 Gateway 모델의 문제점은 Forking 및 Security 문제가 있다.&lt;br /&gt;
&lt;br /&gt;
* Application Server Model&lt;br /&gt;
: SIP 시그널시 Early Media에 대한 Offer/Answer를 가능하게 한다. RFC 3959에 정의된 Content-Disposition 헤더에 Session 또는 Early-session의 새로운 Disposition Type을 설정하여 Early-Media가 가능하게 한다.&lt;br /&gt;
&lt;br /&gt;
=== Ringback Tone 재생 정책 ===&lt;br /&gt;
PSTN 상에서 수신자가 Alert 메시지를 보내면 Ringback Tone은 PBX에 의해 재생된다. SIP의 경우 UAS(수신자)에 의해 Ringback 이 재생되지 않으면, UAC에서 자체적으로 Ringback tone을 재생하기로 되어 있다. 만일, Announcement 또는 Special Ringing Tone이 UAS에 재생이 되면, UAC는 자체적인 재생을 중단하고 UAS로 부터 오는 Media를 재생하는 것이 일반적이다. 그러나, UAS가 Early Media 를 전송하려는 의도 없이 Early Offer를 진행하기도 하고, Reliable Provisional Response 없이 Ealry Media를 전송하기도 하기 때문에 UAC는 쉽게 Local Ringback Tone을 재생해야할 지 말아야할 지를 결정할 수 없다.&lt;br /&gt;
&lt;br /&gt;
Local Ringback Tone 재생에 관련해서 다음과 같은 정책을 구현해야 한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
- 180 Ringing 을 받지 않았다면, Local Ringing 을 재생하지 않는다.&lt;br /&gt;
- 180 Ringing 을 받았으나 수신되는 Media 패킷이 없다면, Local Ringing을 재생한다.&lt;br /&gt;
- 180 Ringing 을 받았으면서 Media 패킷이 수신된다면, Media를 재생하고 Local Ringing을 재생하지 않는다.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
180 Ringing은 수신측이 전화기가 울리고 있음을 의미한다.&lt;br /&gt;
&lt;br /&gt;
=== Application Server Model 상에서의 Early media ===&lt;br /&gt;
Early Media Session 은 지금까지 이야기한 Regual Session에서 함께 처리된다. 그러나, Application Server Model은 Early Media를 위한 별도의 절차를 수행한다. 따라서, 하나의 호 시그널링에서 Early Media Session과 Regular Session 을 동시에 Offer/Answer를 수행한 후 전환한다. 이 때, Content-Disposition Header 에 Session 과 Early-session 이라고 하여 각각의 쓰임새를 구분한다.&lt;br /&gt;
&lt;br /&gt;
Early session 이 Regular session 으로 전환될 때, 코덱을 변경할 필요가 없도록 하기 위하여 Early Media session 과 Media session은 같은 코덱을 사용하는 것을 권장한다. RFC 3959의 예제를 보자.&lt;br /&gt;
&lt;br /&gt;
[[File:Sdp rfc 3959.png]]&lt;br /&gt;
&lt;br /&gt;
앨리스는 밥에게 Contect-Disposition: session 헤더를 INVITE에 추가해서 전송한다. 이 헤더의 의미는 INVITE에 포함된 Offer는 Regular session 에 대한 것임을 표시한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844730 2890844731 IN IP4 host.sip.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.1&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 20000 RTP/AVP 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
이후 밥은 183 Session Progress를 앨리스에게 전송한다. 이때, 다중 메시지가 포함되었음을 의미하는 Content-Type:multipart/mixed 라는 정보를 함께 보낸다. Content-Disposition 헤더를 보면 session과 early-session 두가지가 있는데, 첫번째 바운더리인 Content-Disposition:Session은 Regular session에 대한 것으로 기존의 INVITE with offer에 대한 answer의 내용이다. 두번째 바운더리인 Content-Disposition:early-session은 Early Media를 위한 Offer이다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Content-Type: multipart/mixed; boundary=&amp;quot;boundary1&amp;quot;&lt;br /&gt;
Content-Length: 401&lt;br /&gt;
--boundary1&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=Bob 2890844725 2890844725 IN IP4 host.sip.org&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.2&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 30000 RTP/AVP 0&lt;br /&gt;
&lt;br /&gt;
--boundary1&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: early-session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=Bob 2890844714 2890844714 IN IP4 host.sip.org&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.2&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 30002 RTP/AVP 0&lt;br /&gt;
&lt;br /&gt;
--boundary1--&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
앨리스는 INVITE에 대한 200 OK (수화기를 드는 행동)를 밥으로 받기 전까지 Early-Offer에 대한 Answer를 할 수 있는 방법이 없다. 따라서 이때 PRACK 이 필요하다. 이 메소드를 통해 아래와 같이 Early Offer 에 대한 Answer 를 수행한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Content-Type: application/sdp&lt;br /&gt;
Content-Disposition: early-session&lt;br /&gt;
&lt;br /&gt;
v=0&lt;br /&gt;
o=alice 2890844717 2890744717 IN IP4 host.sip.com&lt;br /&gt;
s=&lt;br /&gt;
c=IN IP4 192.0.2.1&lt;br /&gt;
t=0 0&lt;br /&gt;
m=audio 20002 RTP/AVP 0&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
PRACK에 대한 200 OK 를 받게 되면, Early Media를 통해 대화가 가능하다. 이미 Regular session 과 Early session 에 대한 Offer / Answer가 완료되었다.&lt;br /&gt;
&lt;br /&gt;
밥이 수화기를 들어 200 OK 를 전송할 때, Early session 은 Regular session 으로 전환이 이루어진다.&lt;br /&gt;
&lt;br /&gt;
== Delayed(Late) Media in SDP (RFC 3959, RFC 3960) ==&lt;br /&gt;
SDP Delayed Offer 는 INVITE 메시지에 SDP OFFER 를 전달하지 않고, 180 RINGING 이나 200 OK 에 SDP OFFER 를 전달하는 방식이다.&lt;br /&gt;
&lt;br /&gt;
SDP ANSWER 는 ACK 와 함께 전달한다. Delayed OFFER 는 수신자가 SDP 협상의 주도권을 가진다. CISCO 제품들은 DELAYED OFFER 를 사용한다.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://www.3cx.com/blog/voip-howto/sdp-voip/&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:SIP protocol]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Sipexer&amp;diff=3969</id>
		<title>Sipexer</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Sipexer&amp;diff=3969"/>
		<updated>2023-08-30T01:21:31Z</updated>

		<summary type="html">&lt;p&gt;Pchero: Created page with &amp;quot;== Overview == sipexer - Modern and flexible SIP (RFC3261) command line tool 내용 정리.  == OPTIONS ping == &amp;lt;pre&amp;gt; # /usr/local/bin/sipexer -nagios -fu heartbeat 10.75.0.42:5060  [info] [sipexer.go:1578] main.SIPExerDialogLoop(): local socket address: 10.75.0.42:36617 (udp) [info] [sipexer.go:1579] main.SIPExerDialogLoop(): local via address: 10.75.0.42:36617 [info] [sipexer.go:1580] main.SIPExerDialogLoop(): sending to udp 10.75.0.42:5060: --- OPTIONS sip:10.75.0.4...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
sipexer - Modern and flexible SIP (RFC3261) command line tool 내용 정리.&lt;br /&gt;
&lt;br /&gt;
== OPTIONS ping ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
# /usr/local/bin/sipexer -nagios -fu heartbeat 10.75.0.42:5060&lt;br /&gt;
&lt;br /&gt;
[info] [sipexer.go:1578] main.SIPExerDialogLoop(): local socket address: 10.75.0.42:36617 (udp)&lt;br /&gt;
[info] [sipexer.go:1579] main.SIPExerDialogLoop(): local via address: 10.75.0.42:36617&lt;br /&gt;
[info] [sipexer.go:1580] main.SIPExerDialogLoop(): sending to udp 10.75.0.42:5060: [[---&lt;br /&gt;
OPTIONS sip:10.75.0.42:5060 SIP/2.0&lt;br /&gt;
Via: SIP/2.0/UDP 10.75.0.42:36617;rport;branch=z9hG4bKSG.3b6e77cc-dbf8-4f58-9809-7bca9c3f9272&lt;br /&gt;
From: &amp;lt;sip:heartbeat@localhost&amp;gt;;tag=2d5b7fec-6f21-45e6-a20c-b2ae39535657&lt;br /&gt;
To: &amp;lt;sip:bob@localhost&amp;gt;&lt;br /&gt;
Call-ID: 9a11ff73-de7a-4a19-a0e4-38fa8757ea66&lt;br /&gt;
CSeq: 514429 OPTIONS&lt;br /&gt;
Date: Wed, 30 Aug 2023 00:40:44 UTC&lt;br /&gt;
User-Agent: SIPExer v1.1.0&lt;br /&gt;
Max-Forwards: 10&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[info] [sipexer.go:1582] main.SIPExerDialogLoop(): ---]]&lt;br /&gt;
&lt;br /&gt;
[info] [sipexer.go:1633] main.SIPExerDialogLoop(): response-received: from=10.75.0.42:5060 bytes=438 data=[[---&lt;br /&gt;
SIP/2.0 200 OK&lt;br /&gt;
Via: SIP/2.0/UDP 10.75.0.42:36617;rport=36617;branch=z9hG4bKSG.3b6e77cc-dbf8-4f58-9809-7bca9c3f9272;received=10.75.0.42&lt;br /&gt;
From: &amp;lt;sip:heartbeat@localhost&amp;gt;;tag=2d5b7fec-6f21-45e6-a20c-b2ae39535657&lt;br /&gt;
To: &amp;lt;sip:bob@localhost&amp;gt;;tag=8cc31d58288b2b802886d8395a3eac46.7aa55e46&lt;br /&gt;
Call-ID: 9a11ff73-de7a-4a19-a0e4-38fa8757ea66&lt;br /&gt;
CSeq: 514429 OPTIONS&lt;br /&gt;
Accept: */*&lt;br /&gt;
Accept-Encoding: &lt;br /&gt;
Accept-Language: en&lt;br /&gt;
Supported: &lt;br /&gt;
Content-Length: 0&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[info] [sipexer.go:1635] main.SIPExerDialogLoop(): ---]]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://github.com/miconda/sipexer&lt;br /&gt;
&lt;br /&gt;
[[category:command/utility]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Rtpengine&amp;diff=3968</id>
		<title>Rtpengine</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Rtpengine&amp;diff=3968"/>
		<updated>2023-08-09T03:43:02Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Control scripts */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
RTPEngine 내용 정리.&lt;br /&gt;
&lt;br /&gt;
== Basic ==&lt;br /&gt;
RTPEngine 은 RTP media stream proxy 서버이다.&lt;br /&gt;
&lt;br /&gt;
== Installation ==&lt;br /&gt;
Required packages&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
pkg-config&lt;br /&gt;
GLib including GThread and GLib-JSON version 2.x&lt;br /&gt;
zlib&lt;br /&gt;
OpenSSL&lt;br /&gt;
PCRE library&lt;br /&gt;
XMLRPC-C version 1.16.08 or higher&lt;br /&gt;
hiredis library&lt;br /&gt;
gperf&lt;br /&gt;
libcurl version 3.x or 4.x&lt;br /&gt;
libevent version 2.x&lt;br /&gt;
libpcap&lt;br /&gt;
libsystemd&lt;br /&gt;
spandsp&lt;br /&gt;
MySQL or MariaDB client library (optional for media playback and call recording daemon)&lt;br /&gt;
libiptc library for iptables management (optional)&lt;br /&gt;
ffmpeg codec libraries for transcoding (optional) such as libavcodec, libavfilter, libswresample&lt;br /&gt;
bcg729 for full G.729 transcoding support (optional)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
추가적으로 다음의 패키지들도 필요하다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
libconfig-tiny-perl&lt;br /&gt;
module-assistant&lt;br /&gt;
libxmlrpc-c++8-dev&lt;br /&gt;
linux-*-$(uname -r)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
때때로 아래와 같은 에러가 나오면서 실행이 안되는 경우가 있는데, 이럴땐, linux-*-$(uname -r) 패키지 설치 후 다시 빌드를 진행하면 해결된다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[1581510173.595075] ERR: FAILED TO CREATE KERNEL TABLE 0 (No such file or directory), KERNEL FORWARDING DISABLED&lt;br /&gt;
[1581510173.595925] CRIT: Fatal error: Failed to open UDP control connection port&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Kernel Module ==&lt;br /&gt;
The kernel module supports multiple forwarding tables which are identified through their ID number. By default, up to 64 forwarding tables can be created and used, giving them the ID numbers 0 through 63.&lt;br /&gt;
&lt;br /&gt;
Each forwarding table can be thought of a separated proxy instance. Each running instance of the rtpengine daemon controls one such table, and each table can only be controlled by one running instance of the daemon at any given time. In the most common setup, there will be only a single instance of the daemon running and there will be only a single forward table in use, with ID zero.&lt;br /&gt;
&lt;br /&gt;
The kernel module can be loaded with the command.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ modprove xt_RTPENGINE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
With the module loaded, a new directory will appear in /proc, namely /proc/rtpengine/. After loading, the directory will contain only two pseudo-fils, control and list. The control file is write-only and is used to create and delete forwarding tables, while the list file is read-only and will produce a list of currently active forwarding tables. With no tables active, it will produce an empty output.&lt;br /&gt;
&lt;br /&gt;
The control pseudo-file supports to commands, add and del, each followed by the forwarding table ID number. To manually create a forwarding. table with ID 42, the following command can be used.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ echo 'add 42' &amp;gt; /proc/rtpengine/control&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
After this, the list pseudo-filie will produce the single line 42 as output. This will also create a directory called 42 in /proc/rtpengine/, which contains additional pseudo-files to control this particular forwarding table.&lt;br /&gt;
&lt;br /&gt;
To delete this forwarding table, the command 'del 42' can be issued like above. This will only if no rtpengine daemon is currently running and controlling this table.&lt;br /&gt;
&lt;br /&gt;
Each subdirectory /proc/rtpengine/$ID/ corresponding to each forwarding table contains the pseudo-files blast, control, list and status. The control file is write-only while the others are read-only. The control file will be kept open by the rtpengine daemon while it's running&lt;br /&gt;
&lt;br /&gt;
== Control scripts ==&lt;br /&gt;
=== rtpengine-ctl ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ ./rtpengine-ctl&lt;br /&gt;
&lt;br /&gt;
    rtpengine-ctl [ -ip &amp;lt;ipaddress&amp;gt;[:&amp;lt;port&amp;gt;] -port &amp;lt;port&amp;gt; ] &amp;lt;command&amp;gt;&lt;br /&gt;
&lt;br /&gt;
    Supported commands are:&lt;br /&gt;
&lt;br /&gt;
    list [ numsessions | maxsessions | maxopenfiles | sessions [ &amp;lt;callid&amp;gt; | all | own | foreign ] | totals | loglevel ]&lt;br /&gt;
         numsessions           : print the number of sessions&lt;br /&gt;
         maxsessions           : print the number of allowed sessions&lt;br /&gt;
         maxopenfiles          : print the number of allowed open files&lt;br /&gt;
         sessions &amp;lt;callid&amp;gt;     : print detail about one session&lt;br /&gt;
         sessions all          : print one-liner all sessions information&lt;br /&gt;
         sessions own          : print one-liner own sessions information&lt;br /&gt;
         sessions foreign      : print one-liner foreign sessions information&lt;br /&gt;
         totals                : print total statistics&lt;br /&gt;
         timeout               : print timeout parameter&lt;br /&gt;
         silenttimeout         : print silent-timeout parameter&lt;br /&gt;
         finaltimeout          : print final-timeout parameter&lt;br /&gt;
         loglevel              : print current log level&lt;br /&gt;
         redisallowederrors    : print redis-allowed-errors parameter&lt;br /&gt;
         redisdisabletime      : print redis-disable-time parameter&lt;br /&gt;
         redisconnecttimeout   : print redis-connect-timeout parameter&lt;br /&gt;
         rediscmdtimeout       : print redis-cmd-timeout parameter&lt;br /&gt;
         controltos            : print control-tos parameter&lt;br /&gt;
&lt;br /&gt;
    get                        : get is an alias for list, same parameters apply&lt;br /&gt;
&lt;br /&gt;
    terminate [ &amp;lt;callid&amp;gt; | all | own | foreign ]&lt;br /&gt;
         &amp;lt;callid&amp;gt;              : session is immediately terminated&lt;br /&gt;
         all                   : terminates all current sessions&lt;br /&gt;
         own                   : terminates own current sessions&lt;br /&gt;
         foreign               : terminates foreign current sessions&lt;br /&gt;
&lt;br /&gt;
    set [ maxsessions &amp;lt;int&amp;gt; | maxopenfiles &amp;lt;uint&amp;gt; | timeout &amp;lt;uint&amp;gt; | silent_timeout &amp;lt;uint&amp;gt; | final_timeout &amp;lt;uint&amp;gt; | loglevel &amp;lt;uint&amp;gt; ]&lt;br /&gt;
         maxsessions  &amp;lt;int&amp;gt;    : set the max nr of allowed sessions&lt;br /&gt;
         maxopenfiles &amp;lt;uint&amp;gt;   : set the max nr of allowed open files&lt;br /&gt;
         timeout &amp;lt;uint&amp;gt;        : set the --timeout parameter&lt;br /&gt;
         silenttimeout &amp;lt;uint&amp;gt;  : set the --silent-timeout parameter&lt;br /&gt;
         finaltimeout &amp;lt;uint&amp;gt;   : set the --final-timeout parameter&lt;br /&gt;
         loglevel &amp;lt;uint&amp;gt;       : set the log level to new value (1-7)&lt;br /&gt;
         redisallowederrors    : set the --redis-allowed-errors parameter&lt;br /&gt;
         redisdisabletime      : set the --redis-disable-time parameter&lt;br /&gt;
         redisconnecttimeout   : set the --redis-connect-timeout parameter&lt;br /&gt;
         rediscmdtimeout       : set the --redis-cmd-timeout parameter&lt;br /&gt;
         controltos            : set the --control-tos parameter&lt;br /&gt;
&lt;br /&gt;
    params [ start | current | diff ]&lt;br /&gt;
         start                 : lists the initial values of all the configuration file parameters&lt;br /&gt;
         current               : lists the present values of all the configuration file parameters&lt;br /&gt;
         diff                  : compares initial and present values of all the configuration file parameters and lists the updated parameters&lt;br /&gt;
         revert                : reverts the values of all the configuration file parameters to their initial values&lt;br /&gt;
&lt;br /&gt;
    ksadd [ keyspace &amp;lt;uint&amp;gt;]&lt;br /&gt;
         keyspace &amp;lt;uint&amp;gt;       : subscribe to 'keyspace' database&lt;br /&gt;
&lt;br /&gt;
    ksrm [ keyspace &amp;lt;uint&amp;gt;]&lt;br /&gt;
         keyspace &amp;lt;uint&amp;gt;       : unsubscribe to 'keyspace' database&lt;br /&gt;
                               : remove all foreign calls for that 'keyspace'&lt;br /&gt;
&lt;br /&gt;
    kslist                     : print all currently subscribed keyspaces&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
    Return Value:&lt;br /&gt;
    0 on success with output from server side, other values for failure.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
$ /usr/local/src/rtpengine/utils/rtpengine-ctl list sessions all&lt;br /&gt;
callid: 3d73842720964cefbad634027c1f135d-419 | deletionmark:  no | created:  1691552773 | proxy:10.76.0.105:55290 | redis_keyspace:12 | foreign:no&lt;br /&gt;
callid: 4d3df327-1c79-498d-82ad-64994d16e60d | deletionmark:  no | created:  1691552770 | proxy:10.97.76.4:59999 | redis_keyspace:12 | foreign:no&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://github.com/sipwise/rtpengine&lt;br /&gt;
&lt;br /&gt;
[[category:system]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=STIR/SHAKEN&amp;diff=3967</id>
		<title>STIR/SHAKEN</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=STIR/SHAKEN&amp;diff=3967"/>
		<updated>2023-08-02T14:39:12Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Validation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
STIR/SHAKEN 내용 정리.&lt;br /&gt;
&lt;br /&gt;
== STIR/SHAKEN ==&lt;br /&gt;
The Federal Communications Commission(FCC) has been encouraging the telecommunications industry to develop a solution to stop robocalls and spoofed calling numbers since 2014. The industry's response has been to develop a new technology standard called STIR(Secure Telephony Identity Revisited) and SHAKEN(Secure Handling of Asserted information using toKENs) which defines how telephone service providers should implement the STIR technology to ensure calling numbers are not spoofed.&lt;br /&gt;
&lt;br /&gt;
== How STIR/SHAKEN works ==&lt;br /&gt;
STIR/SHAKEN uses digital certificates, based on common public key cryptography techniques, to ensure the calling number of a telephone call is secure. In simple terms, each telephone service provider obtains their digital certificate from a certificate authority who is trusted by other telephone service providers. The certificate technology enables the called party to verify that the calling number is accurate and has not been spoofed.&lt;br /&gt;
&lt;br /&gt;
* A SIP INVITE is received by the originating telephone service provider.&lt;br /&gt;
* The originating telephone service provider checks the call source and calling number to determine how to attest for the validity of the calling number.&lt;br /&gt;
** Full Attestation(A): The service provider has authenticated the calling party and they are authorized to use the calling number. An example of this case is a subscriber registered with the originating telephone service provider's software.&lt;br /&gt;
** Partial Attestation(B): The service provider has authenticated the call origination, but cannot verify the call source is authorized to use the calling number. An example of this use case is a telephone number behind an enterprise PBX.&lt;br /&gt;
** Gateway Attestation(C): The service provider has authenticated from where it received the call, but cannot authenticate the call source. An example of this case would be a call received from an international gateway.&lt;br /&gt;
* The originating telephone service provider uses the authentication service to create a SIP identity header. The authentication service could be a third-party service hosted in the cloud software application integrated with the telephone service provider's Softswitch or Session Border Controller(SBC). The SIP identity header contains the following data.&lt;br /&gt;
** Calling number&lt;br /&gt;
** Called number(s)&lt;br /&gt;
** Current timestamp&lt;br /&gt;
** Attestation level&lt;br /&gt;
** Origination identifier&lt;br /&gt;
* The SIP INVITE with the SIP identity header is sent to the terminating telephone service provider. In addition, the identity token may be sent across the internet, around non-SIP call segments, using Out-of-Band SHAKEN.&lt;br /&gt;
* The SIP INVITE with identity header is passed to the verification service.&lt;br /&gt;
* The verification service obtains the digital certificate of the originating telephone service provider from the public certificate repository and begins a multi-step verification process. If all verification steps are successful, then the calling number has not been spoofed.&lt;br /&gt;
** The SIP identity  header is base64 URL decoded and the details are compared to the SIP INVITE message.&lt;br /&gt;
** The public key of the certificate is used to verify the SIP identity header signature.&lt;br /&gt;
** The certificate chain of trust is verified.&lt;br /&gt;
* The verification service returns the result to the terminating service provider's Softswitch or SBC.&lt;br /&gt;
* The call is completed to the called party.&lt;br /&gt;
&lt;br /&gt;
== SIP identity header example ==&lt;br /&gt;
The following is an example SIP INVITE message with an identity header.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
INVITE sip:18001234567@example.com:5060 SIP/2.0&lt;br /&gt;
Via: SIP/2.0/UDP example.com:5060&lt;br /&gt;
From: &amp;quot;Alice&amp;quot; &amp;lt;sip:14045266060@5.6.7.8:5060&amp;gt;;tag=123456789&lt;br /&gt;
To: &amp;quot;Bob&amp;quot; &amp;lt;sip:18001234567@1.2.3.4:5060&amp;gt;&lt;br /&gt;
Call-ID: 1-12345@5.6.7.8&lt;br /&gt;
CSeq: 1 INVITE&lt;br /&gt;
Max-Forwards: 70&lt;br /&gt;
Identity: eyJhbGciOiAiRVMyNTYiLCJwcHQiOiAic2hha2VuIiwidHlwIjogInBhc3Nwb3J0IiwieDV1IjogImh0dHBzOi8vY2VydGlmaWNhdGVzLmNsZWFyaXAuY29tL2IxNWQ3Y2M5LTBmMjYtNDZjMi04M2VhLWEzZTYzYTgyZWMzYS83Y2M0ZGI2OTVkMTNlZGFkYTRkMWY5ODYxYjliODBmZS5jcnQifQ.eyJhdHRlc3QiOiAiQSIsImRlc3QiOiB7InRuIjogWyIxNDA0NTI2NjA2MCJdfSwiaWF0IjogMTU0ODg1OTk4Miwib3JpZyI6IHsidG4iOiAiMTgwMDEyMzQ1NjcifSwib3JpZ2lkIjogIjNhNDdjYTIzLWQ3YWItNDQ2Yi04MjFkLTMzZDVkZWVkYmVkNCJ9.S_vqkgCk88ee9rtk89P6a6ru0ncDfSrdb1GyK_mJj-10hsLW-dMF7eCjDYARLR7EZSZwiu0fd4H_QD_9Z5U2bg;info=&amp;lt;https://certificates.clearip.com/b15d7cc9-0f26-46c2-83ea-a3e63a82ec3a/7cc4db695d13edada4d1f9861b9b80fe.crt&amp;gt;alg=ES256;ppt=shaken&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Validation ==&lt;br /&gt;
It is not easy to validate the stir/shaken without third parties.&lt;br /&gt;
&lt;br /&gt;
Test numbers. These numbers are playing back the received attestation level of the call.&lt;br /&gt;
* +1 202 539 1419&lt;br /&gt;
* +1 302 257 7304&lt;br /&gt;
&lt;br /&gt;
Also, the IDT Express provides STIR/SHAKEN test tool.&lt;br /&gt;
* https://www.idtexpress.com/tools/stir-shaken-tool/&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* Understanding STIR/SHAKEN - https://transnexus.com/whitepapers/understanding-stir-shaken/&lt;br /&gt;
&lt;br /&gt;
[[category:Telephony]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=STIR/SHAKEN&amp;diff=3966</id>
		<title>STIR/SHAKEN</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=STIR/SHAKEN&amp;diff=3966"/>
		<updated>2023-08-02T14:38:56Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Validation */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
STIR/SHAKEN 내용 정리.&lt;br /&gt;
&lt;br /&gt;
== STIR/SHAKEN ==&lt;br /&gt;
The Federal Communications Commission(FCC) has been encouraging the telecommunications industry to develop a solution to stop robocalls and spoofed calling numbers since 2014. The industry's response has been to develop a new technology standard called STIR(Secure Telephony Identity Revisited) and SHAKEN(Secure Handling of Asserted information using toKENs) which defines how telephone service providers should implement the STIR technology to ensure calling numbers are not spoofed.&lt;br /&gt;
&lt;br /&gt;
== How STIR/SHAKEN works ==&lt;br /&gt;
STIR/SHAKEN uses digital certificates, based on common public key cryptography techniques, to ensure the calling number of a telephone call is secure. In simple terms, each telephone service provider obtains their digital certificate from a certificate authority who is trusted by other telephone service providers. The certificate technology enables the called party to verify that the calling number is accurate and has not been spoofed.&lt;br /&gt;
&lt;br /&gt;
* A SIP INVITE is received by the originating telephone service provider.&lt;br /&gt;
* The originating telephone service provider checks the call source and calling number to determine how to attest for the validity of the calling number.&lt;br /&gt;
** Full Attestation(A): The service provider has authenticated the calling party and they are authorized to use the calling number. An example of this case is a subscriber registered with the originating telephone service provider's software.&lt;br /&gt;
** Partial Attestation(B): The service provider has authenticated the call origination, but cannot verify the call source is authorized to use the calling number. An example of this use case is a telephone number behind an enterprise PBX.&lt;br /&gt;
** Gateway Attestation(C): The service provider has authenticated from where it received the call, but cannot authenticate the call source. An example of this case would be a call received from an international gateway.&lt;br /&gt;
* The originating telephone service provider uses the authentication service to create a SIP identity header. The authentication service could be a third-party service hosted in the cloud software application integrated with the telephone service provider's Softswitch or Session Border Controller(SBC). The SIP identity header contains the following data.&lt;br /&gt;
** Calling number&lt;br /&gt;
** Called number(s)&lt;br /&gt;
** Current timestamp&lt;br /&gt;
** Attestation level&lt;br /&gt;
** Origination identifier&lt;br /&gt;
* The SIP INVITE with the SIP identity header is sent to the terminating telephone service provider. In addition, the identity token may be sent across the internet, around non-SIP call segments, using Out-of-Band SHAKEN.&lt;br /&gt;
* The SIP INVITE with identity header is passed to the verification service.&lt;br /&gt;
* The verification service obtains the digital certificate of the originating telephone service provider from the public certificate repository and begins a multi-step verification process. If all verification steps are successful, then the calling number has not been spoofed.&lt;br /&gt;
** The SIP identity  header is base64 URL decoded and the details are compared to the SIP INVITE message.&lt;br /&gt;
** The public key of the certificate is used to verify the SIP identity header signature.&lt;br /&gt;
** The certificate chain of trust is verified.&lt;br /&gt;
* The verification service returns the result to the terminating service provider's Softswitch or SBC.&lt;br /&gt;
* The call is completed to the called party.&lt;br /&gt;
&lt;br /&gt;
== SIP identity header example ==&lt;br /&gt;
The following is an example SIP INVITE message with an identity header.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
INVITE sip:18001234567@example.com:5060 SIP/2.0&lt;br /&gt;
Via: SIP/2.0/UDP example.com:5060&lt;br /&gt;
From: &amp;quot;Alice&amp;quot; &amp;lt;sip:14045266060@5.6.7.8:5060&amp;gt;;tag=123456789&lt;br /&gt;
To: &amp;quot;Bob&amp;quot; &amp;lt;sip:18001234567@1.2.3.4:5060&amp;gt;&lt;br /&gt;
Call-ID: 1-12345@5.6.7.8&lt;br /&gt;
CSeq: 1 INVITE&lt;br /&gt;
Max-Forwards: 70&lt;br /&gt;
Identity: eyJhbGciOiAiRVMyNTYiLCJwcHQiOiAic2hha2VuIiwidHlwIjogInBhc3Nwb3J0IiwieDV1IjogImh0dHBzOi8vY2VydGlmaWNhdGVzLmNsZWFyaXAuY29tL2IxNWQ3Y2M5LTBmMjYtNDZjMi04M2VhLWEzZTYzYTgyZWMzYS83Y2M0ZGI2OTVkMTNlZGFkYTRkMWY5ODYxYjliODBmZS5jcnQifQ.eyJhdHRlc3QiOiAiQSIsImRlc3QiOiB7InRuIjogWyIxNDA0NTI2NjA2MCJdfSwiaWF0IjogMTU0ODg1OTk4Miwib3JpZyI6IHsidG4iOiAiMTgwMDEyMzQ1NjcifSwib3JpZ2lkIjogIjNhNDdjYTIzLWQ3YWItNDQ2Yi04MjFkLTMzZDVkZWVkYmVkNCJ9.S_vqkgCk88ee9rtk89P6a6ru0ncDfSrdb1GyK_mJj-10hsLW-dMF7eCjDYARLR7EZSZwiu0fd4H_QD_9Z5U2bg;info=&amp;lt;https://certificates.clearip.com/b15d7cc9-0f26-46c2-83ea-a3e63a82ec3a/7cc4db695d13edada4d1f9861b9b80fe.crt&amp;gt;alg=ES256;ppt=shaken&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Validation ==&lt;br /&gt;
It is not easy to validate the stir/shaken without third parties.&lt;br /&gt;
&lt;br /&gt;
Test numbers. These numbers are playing back the received attestation level of the call.&lt;br /&gt;
* +1 202 539 1419&lt;br /&gt;
* +1 302 257 7304&lt;br /&gt;
&lt;br /&gt;
Also, the IDT provides STIR/SHAKEN test tool.&lt;br /&gt;
* https://www.idtexpress.com/tools/stir-shaken-tool/&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* Understanding STIR/SHAKEN - https://transnexus.com/whitepapers/understanding-stir-shaken/&lt;br /&gt;
&lt;br /&gt;
[[category:Telephony]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=STIR/SHAKEN&amp;diff=3965</id>
		<title>STIR/SHAKEN</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=STIR/SHAKEN&amp;diff=3965"/>
		<updated>2023-08-02T14:38:40Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* See also */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
STIR/SHAKEN 내용 정리.&lt;br /&gt;
&lt;br /&gt;
== STIR/SHAKEN ==&lt;br /&gt;
The Federal Communications Commission(FCC) has been encouraging the telecommunications industry to develop a solution to stop robocalls and spoofed calling numbers since 2014. The industry's response has been to develop a new technology standard called STIR(Secure Telephony Identity Revisited) and SHAKEN(Secure Handling of Asserted information using toKENs) which defines how telephone service providers should implement the STIR technology to ensure calling numbers are not spoofed.&lt;br /&gt;
&lt;br /&gt;
== How STIR/SHAKEN works ==&lt;br /&gt;
STIR/SHAKEN uses digital certificates, based on common public key cryptography techniques, to ensure the calling number of a telephone call is secure. In simple terms, each telephone service provider obtains their digital certificate from a certificate authority who is trusted by other telephone service providers. The certificate technology enables the called party to verify that the calling number is accurate and has not been spoofed.&lt;br /&gt;
&lt;br /&gt;
* A SIP INVITE is received by the originating telephone service provider.&lt;br /&gt;
* The originating telephone service provider checks the call source and calling number to determine how to attest for the validity of the calling number.&lt;br /&gt;
** Full Attestation(A): The service provider has authenticated the calling party and they are authorized to use the calling number. An example of this case is a subscriber registered with the originating telephone service provider's software.&lt;br /&gt;
** Partial Attestation(B): The service provider has authenticated the call origination, but cannot verify the call source is authorized to use the calling number. An example of this use case is a telephone number behind an enterprise PBX.&lt;br /&gt;
** Gateway Attestation(C): The service provider has authenticated from where it received the call, but cannot authenticate the call source. An example of this case would be a call received from an international gateway.&lt;br /&gt;
* The originating telephone service provider uses the authentication service to create a SIP identity header. The authentication service could be a third-party service hosted in the cloud software application integrated with the telephone service provider's Softswitch or Session Border Controller(SBC). The SIP identity header contains the following data.&lt;br /&gt;
** Calling number&lt;br /&gt;
** Called number(s)&lt;br /&gt;
** Current timestamp&lt;br /&gt;
** Attestation level&lt;br /&gt;
** Origination identifier&lt;br /&gt;
* The SIP INVITE with the SIP identity header is sent to the terminating telephone service provider. In addition, the identity token may be sent across the internet, around non-SIP call segments, using Out-of-Band SHAKEN.&lt;br /&gt;
* The SIP INVITE with identity header is passed to the verification service.&lt;br /&gt;
* The verification service obtains the digital certificate of the originating telephone service provider from the public certificate repository and begins a multi-step verification process. If all verification steps are successful, then the calling number has not been spoofed.&lt;br /&gt;
** The SIP identity  header is base64 URL decoded and the details are compared to the SIP INVITE message.&lt;br /&gt;
** The public key of the certificate is used to verify the SIP identity header signature.&lt;br /&gt;
** The certificate chain of trust is verified.&lt;br /&gt;
* The verification service returns the result to the terminating service provider's Softswitch or SBC.&lt;br /&gt;
* The call is completed to the called party.&lt;br /&gt;
&lt;br /&gt;
== SIP identity header example ==&lt;br /&gt;
The following is an example SIP INVITE message with an identity header.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
INVITE sip:18001234567@example.com:5060 SIP/2.0&lt;br /&gt;
Via: SIP/2.0/UDP example.com:5060&lt;br /&gt;
From: &amp;quot;Alice&amp;quot; &amp;lt;sip:14045266060@5.6.7.8:5060&amp;gt;;tag=123456789&lt;br /&gt;
To: &amp;quot;Bob&amp;quot; &amp;lt;sip:18001234567@1.2.3.4:5060&amp;gt;&lt;br /&gt;
Call-ID: 1-12345@5.6.7.8&lt;br /&gt;
CSeq: 1 INVITE&lt;br /&gt;
Max-Forwards: 70&lt;br /&gt;
Identity: eyJhbGciOiAiRVMyNTYiLCJwcHQiOiAic2hha2VuIiwidHlwIjogInBhc3Nwb3J0IiwieDV1IjogImh0dHBzOi8vY2VydGlmaWNhdGVzLmNsZWFyaXAuY29tL2IxNWQ3Y2M5LTBmMjYtNDZjMi04M2VhLWEzZTYzYTgyZWMzYS83Y2M0ZGI2OTVkMTNlZGFkYTRkMWY5ODYxYjliODBmZS5jcnQifQ.eyJhdHRlc3QiOiAiQSIsImRlc3QiOiB7InRuIjogWyIxNDA0NTI2NjA2MCJdfSwiaWF0IjogMTU0ODg1OTk4Miwib3JpZyI6IHsidG4iOiAiMTgwMDEyMzQ1NjcifSwib3JpZ2lkIjogIjNhNDdjYTIzLWQ3YWItNDQ2Yi04MjFkLTMzZDVkZWVkYmVkNCJ9.S_vqkgCk88ee9rtk89P6a6ru0ncDfSrdb1GyK_mJj-10hsLW-dMF7eCjDYARLR7EZSZwiu0fd4H_QD_9Z5U2bg;info=&amp;lt;https://certificates.clearip.com/b15d7cc9-0f26-46c2-83ea-a3e63a82ec3a/7cc4db695d13edada4d1f9861b9b80fe.crt&amp;gt;alg=ES256;ppt=shaken&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Validation ==&lt;br /&gt;
It is not easy to validate the stir/shaken without third parties.&lt;br /&gt;
&lt;br /&gt;
Test numbers. These numbers are playing back the received attestation level of the call.&lt;br /&gt;
* +1 202 539 1419&lt;br /&gt;
* +1 302 257 7304&lt;br /&gt;
&lt;br /&gt;
Also, the IDT provides STIR/SHAKEN test tool.&lt;br /&gt;
https://www.idtexpress.com/tools/stir-shaken-tool/&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* Understanding STIR/SHAKEN - https://transnexus.com/whitepapers/understanding-stir-shaken/&lt;br /&gt;
&lt;br /&gt;
[[category:Telephony]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Asterisk_ari-event&amp;diff=3964</id>
		<title>Asterisk ari-event</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Asterisk_ari-event&amp;diff=3964"/>
		<updated>2023-07-27T03:22:53Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* ChannelStateChange */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Asterisk ARI event 내용 정리&lt;br /&gt;
&lt;br /&gt;
== Channel ==&lt;br /&gt;
A specific communication connection between Asterisk and an Endpoint.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;accountcode&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;name&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Name of the channel (i.e. SIP/foo-0000a7e3)&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;language&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The default spoken language&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;channelvars&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: false,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;object&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Channel variables&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;caller&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;CallerID&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;creationtime&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Date&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Timestamp when channel was created&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;state&amp;quot;: {&lt;br /&gt;
      &amp;quot;allowableValues&amp;quot;: {&lt;br /&gt;
        &amp;quot;valueType&amp;quot;: &amp;quot;LIST&amp;quot;,&lt;br /&gt;
        &amp;quot;values&amp;quot;: [&lt;br /&gt;
          &amp;quot;Down&amp;quot;,&lt;br /&gt;
          &amp;quot;Rsrved&amp;quot;,&lt;br /&gt;
          &amp;quot;OffHook&amp;quot;,&lt;br /&gt;
          &amp;quot;Dialing&amp;quot;,&lt;br /&gt;
          &amp;quot;Ring&amp;quot;,&lt;br /&gt;
          &amp;quot;Ringing&amp;quot;,&lt;br /&gt;
          &amp;quot;Up&amp;quot;,&lt;br /&gt;
          &amp;quot;Busy&amp;quot;,&lt;br /&gt;
          &amp;quot;Dialing Offhook&amp;quot;,&lt;br /&gt;
          &amp;quot;Pre-ring&amp;quot;,&lt;br /&gt;
          &amp;quot;Unknown&amp;quot;&lt;br /&gt;
        ]&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;connected&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;CallerID&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;DialplanCEP&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Current location in the dialplan&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;id&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Unique identifier of the channel.\n\nThis is the same as the Uniqueid field in AMI.&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;Channel&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;A specific communication connection between Asterisk and an Endpoint.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* accountcode: string&lt;br /&gt;
* caller: CallerID&lt;br /&gt;
* channelvars: object(optional): Channel variables&lt;br /&gt;
* connected: CallerID&lt;br /&gt;
* creationtime: Date: Timestamp when channel was created.&lt;br /&gt;
* dialplan: DialplanCEP: Current location in the dialplan.&lt;br /&gt;
* id: string: Unique identifier of the channel.&lt;br /&gt;
: This is the same as the Uniqueid filed in AMI&lt;br /&gt;
&lt;br /&gt;
== BridgeCreated ==&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;BridgeCreated&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2019-03-19T16:48:54.251+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;bridge&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;1b98c21c-98f5-41cc-8fa0-1981bc380501&amp;quot;,&lt;br /&gt;
		&amp;quot;technology&amp;quot;: &amp;quot;simple_bridge&amp;quot;,&lt;br /&gt;
		&amp;quot;bridge_type&amp;quot;: &amp;quot;mixing&amp;quot;,&lt;br /&gt;
		&amp;quot;bridge_class&amp;quot;: &amp;quot;stasis&amp;quot;,&lt;br /&gt;
		&amp;quot;creator&amp;quot;: &amp;quot;Stasis&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;channels&amp;quot;: [],&lt;br /&gt;
		&amp;quot;video_mode&amp;quot;: &amp;quot;none&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:12:32:12&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;test&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== BridgeDestroyed ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;BridgeDestroyed&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2019-03-19T17:00:36.477+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;bridge&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;1209eb30-5823-49b2-b582-a3100e667eb2&amp;quot;,&lt;br /&gt;
		&amp;quot;technology&amp;quot;: &amp;quot;simple_bridge&amp;quot;,&lt;br /&gt;
		&amp;quot;bridge_type&amp;quot;: &amp;quot;mixing&amp;quot;,&lt;br /&gt;
		&amp;quot;bridge_class&amp;quot;: &amp;quot;stasis&amp;quot;,&lt;br /&gt;
		&amp;quot;creator&amp;quot;: &amp;quot;Stasis&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;channels&amp;quot;: [],&lt;br /&gt;
		&amp;quot;video_mode&amp;quot;: &amp;quot;talker&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:84:12:21&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;test&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelCallerId ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
Channel changed Caller ID.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;caller_presentation_txt&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The text representation of the Caller Presentation value.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;caller_presentation&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The integer representation of the Caller Presentation value.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The channel that changed Caller ID.&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;ChannelCallerId&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Channel changed Caller ID.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string(optional) : The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string : Indicates the type of this message.&lt;br /&gt;
* application: string : Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(optional) : Time at which this event was created.&lt;br /&gt;
* caller_presentation: int : The integer representation of the Caller Presentation value.&lt;br /&gt;
* caller_presentation_txt: string : The text representation of the Caller Presentation value.&lt;br /&gt;
* channel: [[#Channel|Channel]] : The channel that changed Caller ID.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;ChannelCallerId&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2019-03-10T20:44:47.240+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;caller_presentation&amp;quot;: 0,&lt;br /&gt;
	&amp;quot;caller_presentation_txt&amp;quot;: &amp;quot;Presentation Allowed, Not Screened&amp;quot;,&lt;br /&gt;
	&amp;quot;channel&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;1552250684.286164&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/pchero-voip-siptrunking-0f922f18&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Ring&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;test_caller&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;pchero-voip-siptrunking&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;test_call&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 2&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2019-03-10T20:44:44.835+0000&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:94:00:11&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;pchero_voip&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelConnectedLine ==&lt;br /&gt;
Channel changed Connected LIne.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The channel whose connected line has changed.&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;ChannelConnectedLine&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Channel changed Connected Line.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string(optional): The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string: Indicates the type of this message.&lt;br /&gt;
* application: string: Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date: Time at which this evvent was created.&lt;br /&gt;
* channel: [[#Channel|Channel]]&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;type&amp;quot;: &amp;quot;ChannelConnectedLine&amp;quot;,&lt;br /&gt;
  &amp;quot;timestamp&amp;quot;: &amp;quot;2019-12-11T12:28:41.318+0000&amp;quot;,&lt;br /&gt;
  &amp;quot;channel&amp;quot;: {&lt;br /&gt;
    &amp;quot;id&amp;quot;: &amp;quot;e64d6d31-6f09-4b3d-b80a-7eb62d1a1d47&amp;quot;,&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;PJSIP/pchero-voip-00224c0b&amp;quot;,&lt;br /&gt;
    &amp;quot;state&amp;quot;: &amp;quot;Down&amp;quot;,&lt;br /&gt;
    &amp;quot;caller&amp;quot;: {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
      &amp;quot;number&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;connected&amp;quot;: {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;anonymous&amp;quot;,&lt;br /&gt;
      &amp;quot;number&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
    &amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
      &amp;quot;context&amp;quot;: &amp;quot;pchero-voip&amp;quot;,&lt;br /&gt;
      &amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
      &amp;quot;priority&amp;quot;: 1,&lt;br /&gt;
      &amp;quot;app_name&amp;quot;: &amp;quot;Stasis&amp;quot;,&lt;br /&gt;
      &amp;quot;app_data&amp;quot;: &amp;quot;pchero_voip,DIALED=1&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;creationtime&amp;quot;: &amp;quot;2019-12-11T12:28:41.111+0000&amp;quot;,&lt;br /&gt;
    &amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:84:00:b6&amp;quot;,&lt;br /&gt;
  &amp;quot;application&amp;quot;: &amp;quot;pchero_voip&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelCreated ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
Notification that a channel has been created.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;ChannelCreated&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Notification that a channel has been created.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string(optional) : The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type : string : Indicates the type of this message.&lt;br /&gt;
* application : string : Name of the application receiving the event.&lt;br /&gt;
* timestamp : Date(optional) : Time at which this event was created.&lt;br /&gt;
* channel : [[#Channel|Channel]]&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;ChannelCreated&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2019-03-06T23:47:09.077+0100&amp;quot;,&lt;br /&gt;
	&amp;quot;channel&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;test_call&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/sipp-uac-00000000&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Down&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;sipp-uac&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 1&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2019-03-06T23:47:09.058+0100&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;00:11:22:33:44:55&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;test&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelDestroyed ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
Notification that a channel has been destroyed.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;cause&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Integer representation of the cause of the hangup&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;cause_txt&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Text representation of the cause of the hangup&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;ChannelDestroyed&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Notification that a channel has been destroyed.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asetrisk_id: string(optional): The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string: Indicates the type of this message.&lt;br /&gt;
* application: string: Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(optional): Time at which this event was created.&lt;br /&gt;
* cause: int: Integer representation of the cause of the hangup. [[Asterisk_hangup_code]]&lt;br /&gt;
* cause_txt: string: Text representation of the cause of the hangup.&lt;br /&gt;
* channel: [[#Channel|Channel]]&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;ChannelDestroyed&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2018-10-23T12:36:11.110+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;cause&amp;quot;: 0,&lt;br /&gt;
	&amp;quot;cause_txt&amp;quot;: &amp;quot;Unknown&amp;quot;,&lt;br /&gt;
	&amp;quot;channel&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;pchero-462475.572686&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/pchero-voip-00001b1f&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Up&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;pchero-voip&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 2&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2018-10-23T12:36:02.671+0000&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;210445d7-73bd-456a-aef2-e0ef847bfa0f&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;pchero_voip&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelDialplan ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
Channel changed location in the dialplan.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;dialplan_app_data&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The data to be passed to the application.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The channel that changed dialplan location.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;dialplan_app&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The application about to be executed.&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;ChannelDialplan&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Channel changed location in the dialplan.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string(optional): The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string: Indicates the type of this message.&lt;br /&gt;
* application: string: Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(optional): Time at which this event was created.&lt;br /&gt;
* channel: [[#Channel|Channel]]: The channel that changed dialplan location.&lt;br /&gt;
* dialplan_app: string: The application about to be executed.&lt;br /&gt;
* dialplan_app_data: string: The data to be passed to the application.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;ChannelDialplan&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2018-10-23T12:36:11.109+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;dialplan_app&amp;quot;: &amp;quot;AppDial2&amp;quot;,&lt;br /&gt;
	&amp;quot;dialplan_app_data&amp;quot;: &amp;quot;(Outgoing Line)&amp;quot;,&lt;br /&gt;
	&amp;quot;channel&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;pchero-462475.572686&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/pchero-voip-00001b1f&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Up&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;pchero-voip&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 2&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2018-10-23T12:36:02.671+0000&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;210445d7-73bd-456a-aef2-e0ef847bfa0f&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;pchero_voip&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelDtmfReceived ==&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;type&amp;quot;: &amp;quot;ChannelDtmfReceived&amp;quot;,&lt;br /&gt;
  &amp;quot;timestamp&amp;quot;: &amp;quot;2020-05-20T06:40:45.663+0000&amp;quot;,&lt;br /&gt;
  &amp;quot;digit&amp;quot;: &amp;quot;9&amp;quot;,&lt;br /&gt;
  &amp;quot;duration_ms&amp;quot;: 100,&lt;br /&gt;
  &amp;quot;channel&amp;quot;: {&lt;br /&gt;
    &amp;quot;id&amp;quot;: &amp;quot;1589956827.6285&amp;quot;,&lt;br /&gt;
    &amp;quot;name&amp;quot;: &amp;quot;PJSIP/call-in-00000633&amp;quot;,&lt;br /&gt;
    &amp;quot;state&amp;quot;: &amp;quot;Up&amp;quot;,&lt;br /&gt;
    &amp;quot;caller&amp;quot;: {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;tttt&amp;quot;,&lt;br /&gt;
      &amp;quot;number&amp;quot;: &amp;quot;pchero&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;connected&amp;quot;: {&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
      &amp;quot;number&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
    &amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
      &amp;quot;context&amp;quot;: &amp;quot;call-in&amp;quot;,&lt;br /&gt;
      &amp;quot;exten&amp;quot;: &amp;quot;9912321321&amp;quot;,&lt;br /&gt;
      &amp;quot;priority&amp;quot;: 2,&lt;br /&gt;
      &amp;quot;app_name&amp;quot;: &amp;quot;Stasis&amp;quot;,&lt;br /&gt;
      &amp;quot;app_data&amp;quot;: &amp;quot;voipbin&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;creationtime&amp;quot;: &amp;quot;2020-05-20T06:40:27.599+0000&amp;quot;,&lt;br /&gt;
    &amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:a4:00:05&amp;quot;,&lt;br /&gt;
  &amp;quot;application&amp;quot;: &amp;quot;voipbin&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelEnteredBridge ==&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;ChannelEnteredBridge&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2019-03-19T16:48:54.265+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;bridge&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;1b98c21c-98f5-41cc-8fa0-1981bc380501&amp;quot;,&lt;br /&gt;
		&amp;quot;technology&amp;quot;: &amp;quot;simple_bridge&amp;quot;,&lt;br /&gt;
		&amp;quot;bridge_type&amp;quot;: &amp;quot;mixing&amp;quot;,&lt;br /&gt;
		&amp;quot;bridge_class&amp;quot;: &amp;quot;stasis&amp;quot;,&lt;br /&gt;
		&amp;quot;creator&amp;quot;: &amp;quot;Stasis&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;channels&amp;quot;: [&amp;quot;d3137253-7118-418c-80f3-1595fbcd75b0&amp;quot;],&lt;br /&gt;
		&amp;quot;video_mode&amp;quot;: &amp;quot;talker&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;channel&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;d3137253-7118-418c-80f3-1595fbcd75b0&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/pchero-voip-00000010&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Up&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;test&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;test&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;pchero-voip&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 1&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2019-03-19T16:48:45.352+0000&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:84:12:32&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;test&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelHangupRequest ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
A hangup was requested on the channel.&lt;br /&gt;
&lt;br /&gt;
ChannelHangupRequest 에는 몇 가지 의미가 있다. &lt;br /&gt;
&lt;br /&gt;
Soft 가 true 로 설정되었을 때는, Hangup() application 이 실행되었거나, Dialplan 에서의 h extension 이 실행되었을 경우 발생한다. h extension 은 명시적으로 지정될 수 도 있고, 묵시적으로 실행이 될 수 있다. 따라서 한번 Dailplan 으로 인입된 Channel 이 종료될 때는 이 이벤트를 발생시킨다.&lt;br /&gt;
&lt;br /&gt;
Soft 가 false 이거나 설정되어 있지 않는 경우는, Hangup Request 를 명시적으로 받았음을 의미한다. ```channel hangup request``` 명령어나 상대방쪽에서 BYE 메시지를 먼저 보냈을 경우가 이에 해당한다.&lt;br /&gt;
&lt;br /&gt;
하지만 뭔가 규칙이 일정하지 않다. ARI 에서 ChannelHangupRequest 이벤트를 사용하고자 할 때는 주의가 필요하다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;soft&amp;quot;: {&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;boolean&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Whether the hangup request was a soft hangup request.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;cause&amp;quot;: {&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;int&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Integer representation of the cause of the hangup.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The channel on which the hangup was requested.&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;ChannelHangupRequest&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;A hangup was requested on the channel.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string (optional) - The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string - Indicates the type of this message.&lt;br /&gt;
* application: string - Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(optional) - Time at which this event was created.&lt;br /&gt;
* cause: int(optional) - Integer representation of the cause of the hangup. [[Asterisk_hangup_code]]&lt;br /&gt;
* channel: Channel - The channel on which the hangup was requested.&lt;br /&gt;
* soft: boolean(optional) - Whether the hangup request was a soft hangup request.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;cause&amp;quot;: 32,&lt;br /&gt;
	&amp;quot;soft&amp;quot;: true,&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;ChannelHangupRequest&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2018-12-02T22:11:29.118+0100&amp;quot;,&lt;br /&gt;
	&amp;quot;channel&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;1543785048.30&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/sippuas-0000000f&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Up&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;sippuas&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 1&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2018-12-02T22:10:48.176+0100&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;08:00:27:2f:9e:d4&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;test&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelLeftBridge ==&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;ChannelLeftBridge&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2019-03-19T16:49:09.571+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;bridge&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;1b98c21c-98f5-41cc-8fa0-1981bc380501&amp;quot;,&lt;br /&gt;
		&amp;quot;technology&amp;quot;: &amp;quot;simple_bridge&amp;quot;,&lt;br /&gt;
		&amp;quot;bridge_type&amp;quot;: &amp;quot;mixing&amp;quot;,&lt;br /&gt;
		&amp;quot;bridge_class&amp;quot;: &amp;quot;stasis&amp;quot;,&lt;br /&gt;
		&amp;quot;creator&amp;quot;: &amp;quot;Stasis&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;channels&amp;quot;: [],&lt;br /&gt;
		&amp;quot;video_mode&amp;quot;: &amp;quot;talker&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;channel&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;d3137253-7118-418c-80f3-1595fbcd75b0&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/pchero-voip-00000010&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Up&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;test&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;pchero-voip&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 1&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2019-03-19T16:48:45.352+0000&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:84:23:12&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;test&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelStateChange ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
Notification of a channel's state change&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;ChannelStateChange&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Notification of a channel's state change.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string(optional): The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string: Indicates the type of this message.&lt;br /&gt;
* application: string: Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(optional): Time at which this event was created.&lt;br /&gt;
* channel: [[#Channel|Channel]]&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;ChannelStateChange&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2018-10-23T12:36:07.628+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;channel&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;pchero-462475.572686&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/pchero-voip-00001b1f&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Ringing&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;pchero-voip&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 1&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2018-10-23T12:36:02.671+0000&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;210445d7-73bd-456a-aef2-e0ef847bfa0f&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;pchero_voip&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelTalkingStarted ==&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
  {&lt;br /&gt;
    &amp;quot;application&amp;quot;: &amp;quot;sdp&amp;quot;,&lt;br /&gt;
    &amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:4b:00:36&amp;quot;,&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;ChannelTalkingStarted&amp;quot;,&lt;br /&gt;
    &amp;quot;timestamp&amp;quot;: &amp;quot;2023-07-27T03:04:30.241+0000&amp;quot;,&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;state&amp;quot;: &amp;quot;Up&amp;quot;,&lt;br /&gt;
      &amp;quot;connected&amp;quot;: {&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Dev Test&amp;quot;,&lt;br /&gt;
        &amp;quot;number&amp;quot;: &amp;quot;+1123&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;PJSIP/registries-0000006f&amp;quot;,&lt;br /&gt;
      &amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
      &amp;quot;caller&amp;quot;: {&lt;br /&gt;
        &amp;quot;number&amp;quot;: &amp;quot;+1123&amp;quot;,&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Dev Test&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;id&amp;quot;: &amp;quot;1690426949.1707&amp;quot;,&lt;br /&gt;
      &amp;quot;creationtime&amp;quot;: &amp;quot;2023-07-27T03:02:29.556+0000&amp;quot;,&lt;br /&gt;
      &amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
      &amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
        &amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
        &amp;quot;app_data&amp;quot;: &amp;quot;sdp,context=register-incoming&amp;quot;,&lt;br /&gt;
        &amp;quot;priority&amp;quot;: 4,&lt;br /&gt;
        &amp;quot;app_name&amp;quot;: &amp;quot;Stasis&amp;quot;,&lt;br /&gt;
        &amp;quot;context&amp;quot;: &amp;quot;common&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    }&lt;br /&gt;
  }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelTalkingFinished ==&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
      &amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
      &amp;quot;connected&amp;quot;: {&lt;br /&gt;
        &amp;quot;number&amp;quot;: &amp;quot;+1123&amp;quot;,&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Dev Test&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;name&amp;quot;: &amp;quot;PJSIP/registries-0000006f&amp;quot;,&lt;br /&gt;
      &amp;quot;id&amp;quot;: &amp;quot;1690426949.1707&amp;quot;,&lt;br /&gt;
      &amp;quot;caller&amp;quot;: {&lt;br /&gt;
        &amp;quot;number&amp;quot;: &amp;quot;+1123&amp;quot;,&lt;br /&gt;
        &amp;quot;name&amp;quot;: &amp;quot;Dev Test&amp;quot;&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;creationtime&amp;quot;: &amp;quot;2023-07-27T03:02:29.556+0000&amp;quot;,&lt;br /&gt;
      &amp;quot;state&amp;quot;: &amp;quot;Up&amp;quot;,&lt;br /&gt;
      &amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
        &amp;quot;app_data&amp;quot;: &amp;quot;sdp,context=register-incoming&amp;quot;,&lt;br /&gt;
        &amp;quot;app_name&amp;quot;: &amp;quot;Stasis&amp;quot;,&lt;br /&gt;
        &amp;quot;priority&amp;quot;: 4,&lt;br /&gt;
        &amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
        &amp;quot;context&amp;quot;: &amp;quot;common&amp;quot;&lt;br /&gt;
      }&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;application&amp;quot;: &amp;quot;sdp&amp;quot;,&lt;br /&gt;
    &amp;quot;timestamp&amp;quot;: &amp;quot;2023-07-27T03:05:15.581+0000&amp;quot;,&lt;br /&gt;
    &amp;quot;duration&amp;quot;: 42839,&lt;br /&gt;
    &amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:4b:00:36&amp;quot;,&lt;br /&gt;
    &amp;quot;type&amp;quot;: &amp;quot;ChannelTalkingFinished&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ChannelVarset ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
Channel variable changed&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;variable&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The variable that changed.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: false,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The channel on which the variable was set.\n\nIf missing, the variable is a global variable.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;value&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The new value of the variable.&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;ChannelVarset&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Channel variable changed.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string(optional): The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string: Indicates the type of this message.&lt;br /&gt;
* application: string: Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(optional): Time at which this event was created.&lt;br /&gt;
* channel: [[#Channel|Channel]](optional): The channel on which the variable was set.&lt;br /&gt;
: If missing, the variable is a global variable.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;variable&amp;quot;: &amp;quot;STASISSTATUS&amp;quot;,&lt;br /&gt;
	&amp;quot;value&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;ChannelVarset&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2018-10-23T12:36:09.602+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;channel&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;pchero-462475.572686&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/pchero-voip-00001b1f&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Up&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;pchero-voip&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 1&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2018-10-23T12:36:02.671+0000&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;210445d7-73bd-456a-aef2-e0ef847bfa0f&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;pchero_voip&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== DeviceStateChanged ==&lt;br /&gt;
Represents the state of a device.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;state&amp;quot;: {&lt;br /&gt;
      &amp;quot;allowableValues&amp;quot;: {&lt;br /&gt;
        &amp;quot;valueType&amp;quot;: &amp;quot;LIST&amp;quot;,&lt;br /&gt;
        &amp;quot;values&amp;quot;: [&lt;br /&gt;
          &amp;quot;UNKNOWN&amp;quot;,&lt;br /&gt;
          &amp;quot;NOT_INUSE&amp;quot;,&lt;br /&gt;
          &amp;quot;INUSE&amp;quot;,&lt;br /&gt;
          &amp;quot;BUSY&amp;quot;,&lt;br /&gt;
          &amp;quot;INVALID&amp;quot;,&lt;br /&gt;
          &amp;quot;UNAVAILABLE&amp;quot;,&lt;br /&gt;
          &amp;quot;RINGING&amp;quot;,&lt;br /&gt;
          &amp;quot;RINGINUSE&amp;quot;,&lt;br /&gt;
          &amp;quot;ONHOLD&amp;quot;&lt;br /&gt;
        ]&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Device's state&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;name&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Name of the device.&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;DeviceState&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Represents the state of a device.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* name: string - Name of the device.&lt;br /&gt;
* state: string - Device's state.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;DeviceStateChanged&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;test&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2018-12-02T22:11:29.121+0100&amp;quot;,&lt;br /&gt;
	&amp;quot;device_state&amp;quot;: {&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/sippuas&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;NOT_INUSE&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;08:00:27:2f:9e:d4&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Dial ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
Dialing state has changed.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;forwarded&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: false,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Channel that the caller has been forwarded to.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;caller&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: false,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The calling channel.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;dialstatus&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Current status of the dialing attempt to the peer.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;forward&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: false,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Forwarding target requested by the original dialed channel.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;dialstring&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: false,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The dial string for calling the peer channel.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;peer&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The dialed channel.&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;Dial&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Dialing state has changed.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string(optional): The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string: Indicates the type of this message.&lt;br /&gt;
* application: string: Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(optional): Time at which this event was created.&lt;br /&gt;
* caller: Channel(optional): The calling channel&lt;br /&gt;
* dialstatus: string: Current status of the dialing attempt to the peer.&lt;br /&gt;
** &amp;lt;empty&amp;gt; : The dialing has begun&lt;br /&gt;
* dialstring: string(optional): The dial string for calling the peer channel.&lt;br /&gt;
* forward: string(optional): Forwarding target requested by the original dialed channel.&lt;br /&gt;
* forwarded: Channel(optional): Channel that the caller has been forwared to.&lt;br /&gt;
* peer: [[#Channel|Channel]]: The dialed channel.&lt;br /&gt;
&lt;br /&gt;
The ARI's Dial event has some different scope compare with AMI's event.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
2016-05-09 15:00 +0000 [205a31f86c]  Mark Michelson &amp;lt;mmichelson@digium.com&amp;gt;&lt;br /&gt;
&lt;br /&gt;
	* Expand the scope of Dial Events&lt;br /&gt;
&lt;br /&gt;
	  Dial events up to this point have come in two flavors&lt;br /&gt;
	  * A Dial event with no status to indicate that dialing has begun&lt;br /&gt;
	  * A Dial event with a status to indicate that dialing has ended&lt;br /&gt;
&lt;br /&gt;
	  With this change, Dial events have been expanded to also give&lt;br /&gt;
	  intermediate events, such as &amp;quot;RINGING&amp;quot;, &amp;quot;PROCEEDING&amp;quot;, and &amp;quot;PROGRESS&amp;quot;.&lt;br /&gt;
	  This is especially useful for ARI dialing, as it gives the application&lt;br /&gt;
	  writer the opportunity to place a channel into an early bridge when&lt;br /&gt;
	  early media is detected.&lt;br /&gt;
&lt;br /&gt;
	  AMI handles these in-progress dial events by sending a new event called&lt;br /&gt;
	  &amp;quot;DialState&amp;quot; that simply indicates that dial state has changed but has&lt;br /&gt;
	  not ended. ARI never distinguished between DialBegin and DialEnd, so no&lt;br /&gt;
	  change was made to the event itself.&lt;br /&gt;
&lt;br /&gt;
	  Another change here relates to dial forwards. A forward-related event&lt;br /&gt;
	  was previously only sent when a channel was successfully able to forward&lt;br /&gt;
	  a call to a new channel. With this set of changes, if forwarding is&lt;br /&gt;
	  blocked, we send a Dial event with a forwarding destination but no&lt;br /&gt;
	  forwarding channel, since we were prevented from creating one. This is&lt;br /&gt;
	  again useful for ARI since application writers can now handle call&lt;br /&gt;
	  forward attempts from within their own application.&lt;br /&gt;
&lt;br /&gt;
	  ASTERISK-25925 #close&lt;br /&gt;
	  Reported by Mark Michelson&lt;br /&gt;
&lt;br /&gt;
	  Change-Id: I42cbec7730d84640a434d143a0d172a740995543&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;Dial&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2018-10-23T12:36:02.673+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;dialstatus&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
	&amp;quot;forward&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
	&amp;quot;dialstring&amp;quot;: &amp;quot;pchero-voip/sip:284712939482@127.0.0.1&amp;quot;,&lt;br /&gt;
	&amp;quot;peer&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;pchero-462475.572686&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/pchero-voip-00001b1f&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Down&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;pchero-voip&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 1&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2018-10-23T12:36:02.671+0000&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;210445d7-73bd-456a-aef2-e0ef847bfa0f&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;pchero_voip&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Playback ==&lt;br /&gt;
Object representing the playback of media to a channel.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;next_media_uri&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: false,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;If a list of URIs is being played, the next media URI to be played back.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;target_uri&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;URI for the channel or bridge to play the media on&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;language&amp;quot;: {&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;For media types that support multiple languages, the language requested for playback.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;state&amp;quot;: {&lt;br /&gt;
      &amp;quot;allowableValues&amp;quot;: {&lt;br /&gt;
        &amp;quot;valueType&amp;quot;: &amp;quot;LIST&amp;quot;,&lt;br /&gt;
        &amp;quot;values&amp;quot;: [&lt;br /&gt;
          &amp;quot;queued&amp;quot;,&lt;br /&gt;
          &amp;quot;playing&amp;quot;,&lt;br /&gt;
          &amp;quot;continuing&amp;quot;,&lt;br /&gt;
          &amp;quot;done&amp;quot;&lt;br /&gt;
        ]&lt;br /&gt;
      },&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Current state of the playback operation.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;media_uri&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;The URI for the media currently being played back.&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;id&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;string&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;ID for this playback operation&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;Playback&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Object representing the playback of media to a channel&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* id: string: ID for this playback operation.&lt;br /&gt;
* language: string(optional): For media types that support multiple languages, the language requested for playback.&lt;br /&gt;
* media_uri: string: The URI for the media currently being played back.&lt;br /&gt;
* next_media_uri: string(optional): If a list of URIs is being played, the next media URI to be played back.&lt;br /&gt;
* state: string: Current state of the playback operation.&lt;br /&gt;
* target_uri: string: URI for the channel or bridge to play the media on.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 {&lt;br /&gt;
	&amp;quot;id&amp;quot;: &amp;quot;1b590cfa-5880-493f-8afc-3f9dfed9022a:3f59e78f-acb2-4bd4-bcd0-23da52c1a2c2&amp;quot;,&lt;br /&gt;
	&amp;quot;media_uri&amp;quot;: &amp;quot;sound:https://pchero21.com/var/spool/asterisk/tts/test.wav&amp;quot;,&lt;br /&gt;
	&amp;quot;target_uri&amp;quot;: &amp;quot;channel:pchero-462475.572686&amp;quot;,&lt;br /&gt;
	&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
	&amp;quot;state&amp;quot;: &amp;quot;playing&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== PlaybackContinuing ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
Event showing the continuation of a media playback operation from one media URI to the next in the list.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;playback&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Playback&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Playback control object&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;PlaybackContinuing&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Event showing the continuation of a media playback operation from one media URI to the next in the list.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string(optional): The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string: Indicates the type of this message.&lt;br /&gt;
* application: string: Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(optional): Time at which this event was created.&lt;br /&gt;
* playback: Playback: Playback control object.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;PlaybackContinuing&amp;quot;,&lt;br /&gt;
	&amp;quot;playback&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;5618648a-985c-4c81-a35e-32f6135b43b9&amp;quot;,&lt;br /&gt;
		&amp;quot;media_uri&amp;quot;: &amp;quot;sound:https://github.com/pchero/asterisk_wavs/raw/master/pcm_samples/example_pcm16_mono_8k_short.wav&amp;quot;,&lt;br /&gt;
		&amp;quot;next_media_uri&amp;quot;: &amp;quot;sound:https://github.com/pchero/asterisk_wavs/raw/master/pcm_samples/example_pcm16_mono_8k_short.wav&amp;quot;,&lt;br /&gt;
		&amp;quot;target_uri&amp;quot;: &amp;quot;channel:test-call&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;continuing&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:84:00:12&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;pchero_voip&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== PlaybackFinished ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
Event showing the completion of a media playback operation.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;playback&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Playback&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Playback control object&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;PlaybackFinished&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Event showing the completion of a media playback operation.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string(optional): The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string: Indicates the type of this message.&lt;br /&gt;
* application: string: Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(optional): Time at which this event was created.&lt;br /&gt;
* playback: Playback: Playback control object.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;PlaybackFinished&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2019-03-07T21:18:53.043+0100&amp;quot;,&lt;br /&gt;
	&amp;quot;playback&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;5b8ea8c2-bf67-4282-9c10-55ebf37a07c1&amp;quot;,&lt;br /&gt;
		&amp;quot;media_uri&amp;quot;: &amp;quot;sound:https://github.com/pchero/asterisk-medias/raw/master/samples_codec/pcm_samples/example-mono_16bit_8khz_pcm.wav&amp;quot;,&lt;br /&gt;
		&amp;quot;target_uri&amp;quot;: &amp;quot;channel:test_call&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;done&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;00:11:22:33:44:55&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;test&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== PlaybackStarted ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
Event showing the start of a media playback operation.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;playback&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Playback&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Playback control object&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;PlaybackStarted&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Event showing the start of a media playback operation.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* aserisk_id: string(optional): The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string: Indicates the type of this message.&lt;br /&gt;
* application: string: Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(optional): Time at which this event was created.&lt;br /&gt;
* playback: Playback: Playback control object.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;PlaybackStarted&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2019-03-07T21:17:56.241+0100&amp;quot;,&lt;br /&gt;
	&amp;quot;playback&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;5b8ea8c2-bf67-4282-9c10-55ebf37a07c1&amp;quot;,&lt;br /&gt;
		&amp;quot;media_uri&amp;quot;: &amp;quot;sound:https://github.com/pchero/asterisk-medias/raw/master/samples_codec/pcm_samples/example-mono_16bit_8khz_pcm.wav&amp;quot;,&lt;br /&gt;
		&amp;quot;target_uri&amp;quot;: &amp;quot;channel:test_call&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;playing&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;00:11:22:33:44:55&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;test&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== RecordingFinished ==&lt;br /&gt;
Event showing the completion of a recording operation.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;recording&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;LiveRecording&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Recording control object&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;RecordingFinished&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Event showing the completion of a recording operation.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* recording: LiveRecording - Recording control object.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;RecordingFinished&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2020-02-10T13:08:18.888+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;recording&amp;quot;: {&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;testrecording-202002101401&amp;quot;,&lt;br /&gt;
		&amp;quot;format&amp;quot;: &amp;quot;wav&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;done&amp;quot;,&lt;br /&gt;
		&amp;quot;target_uri&amp;quot;: &amp;quot;bridge:e9946f5c-2632-4a92-a608-068994d27cbf&amp;quot;,&lt;br /&gt;
		&amp;quot;duration&amp;quot;: 351&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:84:00:68&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;hello-world&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== RecordingStarted ==&lt;br /&gt;
Event showing the start of a recording operation.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
* Channel recording&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;RecordingStarted&amp;quot;,&lt;br /&gt;
	&amp;quot;recording&amp;quot;: {&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;test_call&amp;quot;,&lt;br /&gt;
		&amp;quot;format&amp;quot;: &amp;quot;wav&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;recording&amp;quot;,&lt;br /&gt;
		&amp;quot;target_uri&amp;quot;: &amp;quot;channel:test_call&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:84:00:12&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;pchero&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
* Bridge recording&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;RecordingStarted&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2020-02-10T13:01:22.752+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;recording&amp;quot;: {&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;testrecording-202002101401&amp;quot;,&lt;br /&gt;
		&amp;quot;format&amp;quot;: &amp;quot;wav&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;recording&amp;quot;,&lt;br /&gt;
		&amp;quot;target_uri&amp;quot;: &amp;quot;bridge:e9946f5c-2632-4a92-a608-068994d27cbf&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;42:01:0a:84:00:68&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;hello-world&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== StasisEnd ==&lt;br /&gt;
Bast type: Event&lt;br /&gt;
&lt;br /&gt;
Notification that a channel has left a Stasis application.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;StasisEnd&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Notification that a channel has left a Stasis application.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string(optional): The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string: Indicates the type of this message.&lt;br /&gt;
* application: string: Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(Optional): Time at which this event was created.&lt;br /&gt;
* channel: [[#Channel|Channel]]&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;StasisEnd&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2018-10-23T12:36:11.105+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;channel&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;pchero-462475.572686&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/pchero-voip-00001b1f&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Up&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;pchero-voip&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 2&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2018-10-23T12:36:02.671+0000&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;210445d7-73bd-456a-aef2-e0ef847bfa0f&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;pchero_voip&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== StasisStart ==&lt;br /&gt;
Base type: Event&lt;br /&gt;
&lt;br /&gt;
Notification that a channel has entered a Stasis application.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
  &amp;quot;properties&amp;quot;: {&lt;br /&gt;
    &amp;quot;args&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;List[string]&amp;quot;,&lt;br /&gt;
      &amp;quot;description&amp;quot;: &amp;quot;Arguments to the application&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;replace_channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: false,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;&lt;br /&gt;
    },&lt;br /&gt;
    &amp;quot;channel&amp;quot;: {&lt;br /&gt;
      &amp;quot;required&amp;quot;: true,&lt;br /&gt;
      &amp;quot;type&amp;quot;: &amp;quot;Channel&amp;quot;&lt;br /&gt;
    }&lt;br /&gt;
  },&lt;br /&gt;
  &amp;quot;id&amp;quot;: &amp;quot;StasisStart&amp;quot;,&lt;br /&gt;
  &amp;quot;description&amp;quot;: &amp;quot;Notification that a channel has entered a Stasis application.&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* asterisk_id: string(optional): The unique ID for the Asterisk instance that raised this event.&lt;br /&gt;
* type: string: Indicates the type of this message.&lt;br /&gt;
* application: string: Name of the application receiving the event.&lt;br /&gt;
* timestamp: Date(optional): Time at which this event was created.&lt;br /&gt;
* args: List[string]: Arguments to the application.&lt;br /&gt;
* channel: [[#Channel|Channel]]&lt;br /&gt;
* replace_channel: [[#Channel|Channel]](optional)&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
{&lt;br /&gt;
	&amp;quot;type&amp;quot;: &amp;quot;StasisStart&amp;quot;,&lt;br /&gt;
	&amp;quot;timestamp&amp;quot;: &amp;quot;2018-10-23T12:36:09.603+0000&amp;quot;,&lt;br /&gt;
	&amp;quot;args&amp;quot;: [&amp;quot;PCHERO_DIALED=1&amp;quot;],&lt;br /&gt;
	&amp;quot;channel&amp;quot;: {&lt;br /&gt;
		&amp;quot;id&amp;quot;: &amp;quot;pchero-462475.572686&amp;quot;,&lt;br /&gt;
		&amp;quot;name&amp;quot;: &amp;quot;PJSIP/pchero-voip-00001b1f&amp;quot;,&lt;br /&gt;
		&amp;quot;state&amp;quot;: &amp;quot;Up&amp;quot;,&lt;br /&gt;
		&amp;quot;caller&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;connected&amp;quot;: {&lt;br /&gt;
			&amp;quot;name&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
			&amp;quot;number&amp;quot;: &amp;quot;1337&amp;quot;&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;accountcode&amp;quot;: &amp;quot;&amp;quot;,&lt;br /&gt;
		&amp;quot;dialplan&amp;quot;: {&lt;br /&gt;
			&amp;quot;context&amp;quot;: &amp;quot;pchero-voip&amp;quot;,&lt;br /&gt;
			&amp;quot;exten&amp;quot;: &amp;quot;s&amp;quot;,&lt;br /&gt;
			&amp;quot;priority&amp;quot;: 1&lt;br /&gt;
		},&lt;br /&gt;
		&amp;quot;creationtime&amp;quot;: &amp;quot;2018-10-23T12:36:02.671+0000&amp;quot;,&lt;br /&gt;
		&amp;quot;language&amp;quot;: &amp;quot;en&amp;quot;&lt;br /&gt;
	},&lt;br /&gt;
	&amp;quot;asterisk_id&amp;quot;: &amp;quot;210445d7-73bd-456a-aef2-e0ef847bfa0f&amp;quot;,&lt;br /&gt;
	&amp;quot;application&amp;quot;: &amp;quot;pchero_voip&amp;quot;&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://wiki.asterisk.org/wiki/display/AST/Asterisk+16+REST+Data+Models&lt;br /&gt;
* https://wiki.asterisk.org/wiki/display/AST/Asterisk+17+REST+Data+Models&lt;br /&gt;
&lt;br /&gt;
[[category:asterisk]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Company_info&amp;diff=3963</id>
		<title>Company info</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Company_info&amp;diff=3963"/>
		<updated>2023-07-06T13:33:30Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* International */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
관심있는 회사 정보 정리.&lt;br /&gt;
&lt;br /&gt;
* http://worldvoipproviders.com/countries/&lt;br /&gt;
: 전 세계의 voip 관련 기업 모음&lt;br /&gt;
&lt;br /&gt;
== Belgium ==&lt;br /&gt;
=== Voxbone ===&lt;br /&gt;
* https://www.voxbone.com/&lt;br /&gt;
: Asterisk gerrit 코드 커밋으로 알게됨. 벨기에 주재 IP communication 회사.&lt;br /&gt;
&lt;br /&gt;
== Bulgaria ==&lt;br /&gt;
=== Zoiper ===&lt;br /&gt;
* https://www.zoiper.com&lt;br /&gt;
&lt;br /&gt;
== Canada ==&lt;br /&gt;
=== AllStream ===&lt;br /&gt;
* https://allstream.com/&lt;br /&gt;
* Asterisk IRC 로 알게됨.&lt;br /&gt;
&lt;br /&gt;
=== Jive ===&lt;br /&gt;
* https://jive.com/&lt;br /&gt;
* 대기업. 사원수가 많음.&lt;br /&gt;
&lt;br /&gt;
=== Wazo ===&lt;br /&gt;
* http://wazo.community&lt;br /&gt;
* Asterisk-amqp 관련 자료를 찾다가 발견함. Astricon2017 에서 발표한 내용이 있음.&lt;br /&gt;
&lt;br /&gt;
=== Textnow ===&lt;br /&gt;
* http://textnow.com&lt;br /&gt;
&lt;br /&gt;
== China ==&lt;br /&gt;
=== Hiastar ===&lt;br /&gt;
* http://www.hiastar.com/&lt;br /&gt;
&lt;br /&gt;
== Denmark ==&lt;br /&gt;
=== Dixa ===&lt;br /&gt;
* https://www.dixa.com/&lt;br /&gt;
* Denmark&lt;br /&gt;
Telephony, Live chat, Virtual Service Assistant 등의 통합 시스템 서비스를 제공하는 회사.&lt;br /&gt;
&lt;br /&gt;
=== ipnordic ===&lt;br /&gt;
* http://www.ipnordic.dk&lt;br /&gt;
* Denmark&lt;br /&gt;
덴마크 통신 회사.&lt;br /&gt;
&lt;br /&gt;
Asterisk 기반의 CTI 서비스를 제공한다.&lt;br /&gt;
&lt;br /&gt;
=== ipvision ===&lt;br /&gt;
덴마크 통신회사.&lt;br /&gt;
&lt;br /&gt;
Asterisk 기반의 CTI 서비스를 제공한다.&lt;br /&gt;
: http://ipvision.dk&lt;br /&gt;
&lt;br /&gt;
=== zylinc ===&lt;br /&gt;
* http://www.zylinc.com/&lt;br /&gt;
* Denmark&lt;br /&gt;
UC 관련 컨택트 센터 프로그램을 개발하는 덴마크 회사.&lt;br /&gt;
&lt;br /&gt;
=== Unitel ===&lt;br /&gt;
* https://uni-tel.dk/&lt;br /&gt;
* Denmark&lt;br /&gt;
Denmark UC 관련 통신 회사.&lt;br /&gt;
&lt;br /&gt;
== France ==&lt;br /&gt;
=== Prescom ===&lt;br /&gt;
* http://www.prescom.net&lt;br /&gt;
&lt;br /&gt;
== Germany ==&lt;br /&gt;
=== AVM GmbH ===&lt;br /&gt;
* https://www.avm.de/&lt;br /&gt;
* https://en.wikipedia.org/wiki/AVM_GmbH&lt;br /&gt;
인터넷 공유기 및 스위치, 라우터 등을 판매함.&lt;br /&gt;
&lt;br /&gt;
=== C2Call GmbH ===&lt;br /&gt;
* http://www.c2call.com/&lt;br /&gt;
* https://en.wikipedia.org/wiki/C2Call_GmbH&lt;br /&gt;
2008 년 세워진 독일 VoIP 관련 회사.&lt;br /&gt;
&lt;br /&gt;
=== Nfon ===&lt;br /&gt;
* http://nfon.com/&lt;br /&gt;
* https://en.wikipedia.org/wiki/Nfon&lt;br /&gt;
Cloud-based telephone systems.&lt;br /&gt;
&lt;br /&gt;
=== SIPGate ===&lt;br /&gt;
* http://www.sipgate.de/&lt;br /&gt;
* https://en.wikipedia.org/wiki/Sipgate&lt;br /&gt;
&lt;br /&gt;
== India ==&lt;br /&gt;
=== Plivo ===&lt;br /&gt;
* https://www.plivo.com/&lt;br /&gt;
* India&lt;br /&gt;
기업용 SMS, Voice calls 서비스를 제공하는 회사.&lt;br /&gt;
&lt;br /&gt;
== Korea ==&lt;br /&gt;
=== bridge mobile ===&lt;br /&gt;
국내에 있는 mVoIP 관련 회사.&lt;br /&gt;
* Asterisk 를 이용하여 서비스를 제공함.&lt;br /&gt;
: http://rocketpun.ch/company/playmobs/&lt;br /&gt;
: http://www.bridge.co/&lt;br /&gt;
&lt;br /&gt;
=== couchgram ===&lt;br /&gt;
국내 모바일 기반 통신 프로그램 개발 회사.&lt;br /&gt;
: http://www.couchgram.com/&lt;br /&gt;
&lt;br /&gt;
=== herit ===&lt;br /&gt;
통신관련 솔루션 회사.&lt;br /&gt;
Parlay X 관련 정보 검색 중 발견. 기술력이 높아보임.&lt;br /&gt;
: http://www.herit.net/kr/index.php&lt;br /&gt;
&lt;br /&gt;
=== hyper connect ===&lt;br /&gt;
WebRTC 관련 회사.&lt;br /&gt;
회사 문화가 매우 좋아보임. :)&lt;br /&gt;
: https://hyperconnect.com/&lt;br /&gt;
&lt;br /&gt;
=== safeDKorea ===&lt;br /&gt;
* http://www.safedkorea.co.kr/&lt;br /&gt;
* Korea&lt;br /&gt;
Raspberry pi + Asterisk 솔루션 제작 업체.&lt;br /&gt;
&lt;br /&gt;
=== sendbird ===&lt;br /&gt;
* https://sendbird.com/&lt;br /&gt;
* https://platum.kr/archives/121122&lt;br /&gt;
* 2020 1월 기준, 약 100여명 정도가 한국과 미국에서 근무중. 실리콘 벨리에 본사가 있음.&lt;br /&gt;
* https://sendbird.com/about 회사 마인드가 괜찮음.&lt;br /&gt;
&lt;br /&gt;
=== trys ===&lt;br /&gt;
국내 VoIP 관련 업체.&lt;br /&gt;
: http://www.trys.co.kr&lt;br /&gt;
&lt;br /&gt;
=== zoyi ===&lt;br /&gt;
리테일 컴퓨팅이라는 서비스를 제공하는 회사.&lt;br /&gt;
* 임베디드와 클라우드를 이용하여 재미있는 기업형(?) 서비스를 제공한다.&lt;br /&gt;
* 무엇보다 연봉 테이블을 공개한 것이 매우 인상적.&lt;br /&gt;
: http://zoyi.co/&lt;br /&gt;
&lt;br /&gt;
=== 보이스로코 ===&lt;br /&gt;
국내 무료 통화 제공 업체.&lt;br /&gt;
* 아이폰/안드로이드로 이미 출시된 앱이 있으며(2015,04 기준), 앞으로도 많은 발전 가능성이 보임.&lt;br /&gt;
: http://www.voiceloco.com/&lt;br /&gt;
&lt;br /&gt;
=== IMC Games ===&lt;br /&gt;
국내 게임 개발 명가. 김학규 프로그래머가 프로듀서로 있음.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
지금까지 필자가 읽었던 컴퓨터 책 중 최고의 책 한 권만을 꼽으라면 단연 본서 Pragmatic Programmer를 꼽을 것이다. &lt;br /&gt;
그만큼 필자의 프로그래밍 인생에 많은 영향을 주었고, 다시 매니저가 된 필자로 하여금 팀원들에게 영향을 끼치고 있기 때문이다. &lt;br /&gt;
&lt;br /&gt;
프로그래밍의 세계에는 많은 이론들이 생겨나고, 저마다 혁신적인 방법론을 제창하기도 하며, 프로그래밍의 왕도에 대한 수많은 논란이 벌어지고 있다. &lt;br /&gt;
도대체 더 나은 소프트웨어를 만들려면 어떻게 해야 하는 것일까? &lt;br /&gt;
지금까지 우리는 구조적 프로그래밍, OOP, Generic 프로그래밍, 컴포넌트 기반 개발, 디자인 패턴, 리팩터링, Aspect Oriented Programming 등등 &lt;br /&gt;
수많은 이론과 방법을 접하고 있으며, 앞으로도 새로운 이론들은 계속 나올 것으로 보인다. &lt;br /&gt;
하지만 이러한 많은 이론들이 추구하는바 더 나은 소프트웨어를 만드는 더 나은 방법의 공통 요소가 있는 것은 아닐까? 그런 것이 있다면 어떤 것일까? &lt;br /&gt;
&lt;br /&gt;
본서는 그 공통요소에 대해 명쾌한 비유와 설명, 실용적인 예시로 필자의 궁금증을 풀어주었다. &lt;br /&gt;
DRY나 직교화 같은 개념을 마음속으로 이해하고 나면 위에 예로 들었던 수많은 방법론들이 어떤 필요성에서 출발해서 왜 이런 형태로 존재하고, 또 앞으로는 어떻게 나아갈 것인지에 대한 통찰을 얻을 수 있다. &lt;br /&gt;
깨진 창문이나 삶은 개구리 같은 식의비유를 통해서 팀원들 간 이해의 공감대를 넓힌 것은 정말 가치 있는 일이었다. &lt;br /&gt;
&lt;br /&gt;
필자의 회사에서는 새로운 팀원을 채용하기 전에, 지원자에게 Pragmatic Programmer를 면접보기 전에 읽고 올 것을 요구하고 있다. &lt;br /&gt;
안 읽고 온 사람에게는 책에 있는 것과 똑같은 얘기를 회사에서 해줘야 하기 때문이다. &lt;br /&gt;
&lt;br /&gt;
늦은 감이 있지만 이러한 명저가 한국어로 출간된다는 점에 대해 너무나도 다행스럽게 생각하며, 독자 여러분도 깨달음의 기쁨을 공유할 수 있기를 기원한다.  &lt;br /&gt;
[IMC게임즈 프로듀서 김학규]&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
: http://www.imc.co.kr/&lt;br /&gt;
&lt;br /&gt;
=== 나온웍스(Naon Works) ===&lt;br /&gt;
* http://www.naonworks.com&lt;br /&gt;
국내 SBC(Session Border Controller) 관련 내용을 찾다가 알게 된 회사.&lt;br /&gt;
&lt;br /&gt;
=== 스무디(smoothy) ===&lt;br /&gt;
* https://smoothy.co/ko&lt;br /&gt;
영상 채팅 관련 회사. WebRTC 기반 다자간 영상통화 서버 프로그램 개발 및 인프라 운영.&lt;br /&gt;
&lt;br /&gt;
=== 민코넷(minkonet) ===&lt;br /&gt;
* https://www.minkonet.com/&lt;br /&gt;
WebRTC 기반 게임 스트리밍 서비스 제공.&lt;br /&gt;
&lt;br /&gt;
=== 더블미디어(double media) ===&lt;br /&gt;
* https://www.saramin.co.kr/zf_user/company-info/view?csn=T1MrRzlIMG1ROUNqUFlwRjdtekh1QT09&lt;br /&gt;
&lt;br /&gt;
=== 브로드 씨엔에스(Broad CNS) ===&lt;br /&gt;
* http://www.broadcns.com/&lt;br /&gt;
* Asterisk 기반 콜센터 솔루션 업체.&lt;br /&gt;
* Java, ASP.NET, C#&lt;br /&gt;
&lt;br /&gt;
=== 포지큐브(Posicube) ===&lt;br /&gt;
* https://www.posicube.com/&lt;br /&gt;
* Asterisk + AI 관련 솔루션 개발.&lt;br /&gt;
* 콜센터 관련 프로그램 개발.&lt;br /&gt;
&lt;br /&gt;
=== 아틀라스랩스(atlaslabs) ===&lt;br /&gt;
* https://www.atlaslabs.ai/&lt;br /&gt;
* Asterisk + AI 관련 솔루션 개발.&lt;br /&gt;
&lt;br /&gt;
=== 솔트룩스(saltlux) ===&lt;br /&gt;
* http://www.saltlux.com/&lt;br /&gt;
* Asterisk 사용.&lt;br /&gt;
&lt;br /&gt;
== New zealand ==&lt;br /&gt;
=== Venture voip ===&lt;br /&gt;
* http://venturevoip.com/index.php&lt;br /&gt;
뉴질랜드 회사. Asterisk 메일링 리스트에서 알게됨.&lt;br /&gt;
&lt;br /&gt;
== USA ==&lt;br /&gt;
=== Avoxi ===&lt;br /&gt;
* https://www.avoxi.com/&lt;br /&gt;
* US based call center solution company.&lt;br /&gt;
&lt;br /&gt;
=== Connexo ===&lt;br /&gt;
* http://www.connexo.net&lt;br /&gt;
* Porta Text 메시지(SMS) 전송 서비스.&lt;br /&gt;
github 에서 알게 된 회사. 미국에 본사가 있으며 CTI 쪽 개발을 하는 회사 같다. CTO가 굉장히 github 활동이 왕성해서 알게됨.&lt;br /&gt;
&lt;br /&gt;
=== elastix ===&lt;br /&gt;
UC 관련 교환기 회사(Fronend/Backend).&lt;br /&gt;
: http://www.elastix.com/en/&lt;br /&gt;
: 본사는 3CX. https://www.3cx.com&lt;br /&gt;
&lt;br /&gt;
=== ooma ===&lt;br /&gt;
VoIP 업체. 위키 피디아에도 등재되어 있음. Kamalio/OpenSER, Asterisk, Freeswitch 와 관련한 개발자를 구하고 있음(Senior).&lt;br /&gt;
&lt;br /&gt;
2015.07 $8.5M 규모 나스닥 상장.&amp;lt;ref&amp;gt;http://www.nasdaq.com/article/internet-based-telecom-provider-ooma-sets-terms-for-85-million-ipo-cm493605&amp;lt;/ref&amp;gt;&lt;br /&gt;
&lt;br /&gt;
미국 팔로 알토에 본사 위치.&lt;br /&gt;
: http://www.ooma.com/&lt;br /&gt;
: https://en.wikipedia.org/wiki/Ooma&lt;br /&gt;
&lt;br /&gt;
=== telnyx ===&lt;br /&gt;
2017.05.02 Amsterdam 에서 열린 OpenSIP Summt 에서 알게됨.&lt;br /&gt;
유투브로 시청하던 중, 강단에 찍힌 로고로 알게되었음.&lt;br /&gt;
&lt;br /&gt;
미국 일리노이 주에 위치함. SIP service provider 및 기업용 소프트웨어를 판매하는 것으로 보임.&lt;br /&gt;
: https://telnyx.com/&lt;br /&gt;
&lt;br /&gt;
=== IDT ===&lt;br /&gt;
* https://www.idt.net/&lt;br /&gt;
* Asterisk/Kamailio/... 등등 기반으로 Route provide 서비스 제공.&lt;br /&gt;
&lt;br /&gt;
=== Twilio ===&lt;br /&gt;
* https://www.twilio.com/&lt;br /&gt;
SMS, Voice, Video, Authentication 관련 API 를 제공하는 회사.&lt;br /&gt;
&lt;br /&gt;
=== xcally ===&lt;br /&gt;
Asterisk 기반 Call center solution 회사&lt;br /&gt;
: http://www.xcally.com/&lt;br /&gt;
&lt;br /&gt;
=== NextVR ===&lt;br /&gt;
실시간 VR(Virtual Reality) 개발 회사. 실시간으로 VR이 가능하다면 어떤 것들이 가능해질지 정말 궁금하다. :)&lt;br /&gt;
: http://www.nextvr.com/&lt;br /&gt;
: http://www.nextvr.com/careers/&lt;br /&gt;
: http://www.wsj.com/articles/comcast-time-warner-invest-in-virtual-reality-1447274523 - VR 실시간 스트리밍 업체 NextVR, 350억원 유치&lt;br /&gt;
&lt;br /&gt;
=== Freeswitch ===&lt;br /&gt;
* http://freeswitch.com - freeswitch&lt;br /&gt;
&lt;br /&gt;
=== Kandy ===&lt;br /&gt;
* https://www.kandy.io/&lt;br /&gt;
&lt;br /&gt;
=== Flowroute ===&lt;br /&gt;
* https://www.flowroute.com&lt;br /&gt;
&lt;br /&gt;
=== Switch ===&lt;br /&gt;
* https://www.switch.co/&lt;br /&gt;
&lt;br /&gt;
=== 2600Hz ===&lt;br /&gt;
* https://www.2600hz.com/&lt;br /&gt;
&lt;br /&gt;
=== Tropo ===&lt;br /&gt;
* https://www.tropo.com/products&lt;br /&gt;
&lt;br /&gt;
=== Onsip ===&lt;br /&gt;
* https://www.onsip.com/&lt;br /&gt;
&lt;br /&gt;
=== Orecx ===&lt;br /&gt;
* http://www.orecx.com/&lt;br /&gt;
&lt;br /&gt;
== International ==&lt;br /&gt;
=== Sangoma ===&lt;br /&gt;
* http://www.sangoma.com/&lt;br /&gt;
&lt;br /&gt;
=== Mitel ===&lt;br /&gt;
* http://www.mitel.com&lt;br /&gt;
&lt;br /&gt;
=== Twilio ===&lt;br /&gt;
* https://www.twilio.com&lt;br /&gt;
&lt;br /&gt;
=== Truphone ===&lt;br /&gt;
* https://www.truphone.com&lt;br /&gt;
&lt;br /&gt;
=== Cisco ===&lt;br /&gt;
* https://meraki.cisco.com&lt;br /&gt;
&lt;br /&gt;
=== VoiceTel ===&lt;br /&gt;
* http://www.voicetel.com/&lt;br /&gt;
&lt;br /&gt;
=== Zendesk ===&lt;br /&gt;
* https://www.zendesk.com/jobs/&lt;br /&gt;
&lt;br /&gt;
=== Infobip ===&lt;br /&gt;
* 유럽(Poland)에 HQ가 위치한 CCaaS 기업.&lt;br /&gt;
* 여러 채널(Call/SMS/SNS/...)을 지원하며, API 문서 정리가 잘 되어 있음.(https://www.infobip.com/docs/api)&lt;br /&gt;
* https://www.infobip.com&lt;br /&gt;
&lt;br /&gt;
=== Link mobility ===&lt;br /&gt;
* https://www.linkmobility.com/&lt;br /&gt;
* Norway based CPaas Company.&lt;br /&gt;
&lt;br /&gt;
== Other ==&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:etc]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Mysql_sql&amp;diff=3962</id>
		<title>Mysql sql</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Mysql_sql&amp;diff=3962"/>
		<updated>2023-07-03T02:43:27Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* ETC */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Mysql 사용법 정리.&lt;br /&gt;
&lt;br /&gt;
== Basic ==&lt;br /&gt;
=== Data types ===&lt;br /&gt;
==== datetime ====&lt;br /&gt;
Mysql-5.6 버전 이후부터 사용가능.&lt;br /&gt;
&lt;br /&gt;
=== Naming ===&lt;br /&gt;
Mysql 에서 Table, View 등의 이름을 숫자로 만들 수는 없다. 하지만 숫자-스트링으로는 가능하다. 이 경우 숫자-스트링을 ``로 감싸주면 된다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql&amp;gt; create view 123 as select * from t1 where time=&amp;quot;now&amp;quot;;&lt;br /&gt;
ERROR 1064 (42000): You have an error in your SQL syntax; &lt;br /&gt;
check the manual that corresponds to your MySQL server version for the right syntax to use near '123 as select * from t1 where time=&amp;quot;now&amp;quot;' at line 1&lt;br /&gt;
&lt;br /&gt;
mysql&amp;gt; create view &amp;quot;123&amp;quot; as select * from t1 where time=&amp;quot;now&amp;quot;;&lt;br /&gt;
ERROR 1064 (42000): You have an error in your SQL syntax; &lt;br /&gt;
check the manual that corresponds to your MySQL server version for the right syntax to use near '&amp;quot;123&amp;quot; as select * from t1 where time=&amp;quot;now&amp;quot;' at line 1&lt;br /&gt;
&lt;br /&gt;
mysql&amp;gt; create view '123' as select * from t1 where time=&amp;quot;now&amp;quot;;&lt;br /&gt;
ERROR 1064 (42000): You have an error in your SQL syntax; &lt;br /&gt;
check the manual that corresponds to your MySQL server version for the right syntax to use near ''123' as select * from t1 where time=&amp;quot;now&amp;quot;' at line 1&lt;br /&gt;
&lt;br /&gt;
mysql&amp;gt; create view `123` as select * from t1 where time=&amp;quot;now&amp;quot;;&lt;br /&gt;
Query OK, 0 rows affected (0.10 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== foreign key ==&lt;br /&gt;
Mysql-3.23.43 버전 이후부터는 외래키 지정이 가능하다.&lt;br /&gt;
&lt;br /&gt;
=== Options ===&lt;br /&gt;
* on delete cascade&lt;br /&gt;
부모의 해당키가 삭제되면 자동으로 삭제된다.&lt;br /&gt;
&lt;br /&gt;
* on delete set null&lt;br /&gt;
부모의 해당키가 삭제되면 자동으로 null 로 만든다.&lt;br /&gt;
&lt;br /&gt;
* on update cascade&lt;br /&gt;
부모의 해당키가 삭제되면 자동으로 갱신된다.&lt;br /&gt;
&lt;br /&gt;
=== Example ===&lt;br /&gt;
agent id 를 외래키로 지정을 한 agent_group 테이블이다. agent_group 에 속한 agent 삭제시 같이 삭제되고, 업데이트 시, 같이 업데이트된다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=sql&amp;gt;&lt;br /&gt;
create table agent (&lt;br /&gt;
    id          int not null unique,&lt;br /&gt;
    name        varchar(255),&lt;br /&gt;
&lt;br /&gt;
    primary key(id)&lt;br /&gt;
&lt;br /&gt;
);&lt;br /&gt;
&lt;br /&gt;
create table agent_group(&lt;br /&gt;
    group_id    int not null unique,&lt;br /&gt;
    agent_id    int not null,&lt;br /&gt;
&lt;br /&gt;
    foreign key(agent_id)       references agent(id) on delete cascade on update cascade,&lt;br /&gt;
&lt;br /&gt;
    primary key(group_id, agent_id)&lt;br /&gt;
);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Alter ==&lt;br /&gt;
이미 생성되어 있는 테이블의 내용을 변경한다.&lt;br /&gt;
&lt;br /&gt;
Drop the column from the table.&lt;br /&gt;
&amp;lt;source lang=sql&amp;gt;&lt;br /&gt;
ALTER TABLE table_name DROP column_name;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Add the column to the table&lt;br /&gt;
&amp;lt;source lang=sql&amp;gt;&lt;br /&gt;
ALTER TABLE Customers ADD Email varchar(255);&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=sql&amp;gt;&lt;br /&gt;
ALTER TABLE table_name MODIFY COLUMN column_name datatype;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Update ==&lt;br /&gt;
이미 존재하는 레코드의 값을 변경한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
UPDATE &amp;lt;table&amp;gt; SET &amp;lt;field&amp;gt; = &amp;quot;&amp;lt;value&amp;gt;&amp;quot; WHERE &amp;lt;field&amp;gt; &amp;gt; &amp;quot;&amp;lt;condition&amp;gt;&amp;quot;;&lt;br /&gt;
조건 값보다 큰 필드의 레코드 값을 수정 &lt;br /&gt;
&lt;br /&gt;
UPDATE &amp;lt;table&amp;gt; SET &amp;lt;field&amp;gt; = &amp;quot;&amp;lt;value&amp;gt;&amp;quot; WHERE &amp;lt;field&amp;gt; &amp;lt;&amp;gt; &amp;quot;&amp;lt;condition&amp;gt;&amp;quot;;&lt;br /&gt;
조건 값을 제외한 모든 레코드 값을 수정 &lt;br /&gt;
&lt;br /&gt;
UPDATE &amp;lt;table&amp;gt; SET &amp;lt;field1&amp;gt; = &amp;quot;&amp;lt;value&amp;gt;&amp;quot;, &amp;lt;field2&amp;gt; = &amp;quot;&amp;lt;value&amp;gt;&amp;quot; WHERE &amp;lt;conditions&amp;gt;;&lt;br /&gt;
조건에 맞는 두개의 필드를 바꿈&lt;br /&gt;
&lt;br /&gt;
UPDATE &amp;lt;table&amp;gt; SET &amp;lt;field&amp;gt; = REPLACE(&amp;lt;field&amp;gt;, 'alpha', 'beta')&lt;br /&gt;
필드의 값에 'alpha'라는 단어가 포함 되어 있다면 모두 'beta'로 수정&lt;br /&gt;
&lt;br /&gt;
UPDATE &amp;lt;table&amp;gt; SET &amp;lt;field&amp;gt; = CONCAT(&amp;lt;field&amp;gt;,'alpha') WHERE &amp;lt;conditions&amp;gt;&lt;br /&gt;
조건에 맞는 필드명의 값에 'alpha' 단어를 덧붙임&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Insert ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
INSERT INTO &amp;lt;table&amp;gt; (columns) VALUES (values);&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Delete ==&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
DELETE FROM `table_name` [WHERE condition];&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== View ==&lt;br /&gt;
View 는 임시 테이블로도 번역이 된다. 어떤 테이블에서 특정 자료들만을 관리하는 임의의 테이블을 만들고자할 때, 사용할 수 있다.&lt;br /&gt;
&lt;br /&gt;
=== Create ===&lt;br /&gt;
View 를 생성한다.&lt;br /&gt;
&amp;lt;source lang=sql&amp;gt;&lt;br /&gt;
create view &amp;lt;view_name&amp;gt; as &amp;lt;select_query&amp;gt;;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Show all views ===&lt;br /&gt;
현재 생성되어 있는 모든 view 들을 확인하고자 할 때는 다음의 쿼리를 사용하면 된다.&lt;br /&gt;
&amp;lt;source lang=sql&amp;gt;&lt;br /&gt;
SHOW FULL TABLES IN &amp;lt;database_name&amp;gt; WHERE TABLE_TYPE LIKE 'VIEW';&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Error ==&lt;br /&gt;
&lt;br /&gt;
=== Warnings ===&lt;br /&gt;
Mysql Query 를 실행하다보면 한번씩 Warning 메시지를 확인할 수 있다. 뭔가 경고할만한 내용이 있다는 것인데, 확인하기 위해서는 다음의 명령어를 입력하면 된다&amp;lt;ref&amp;gt;http://dev.mysql.com/doc/refman/5.0/en/show-warnings.html&amp;lt;/ref&amp;gt;.&lt;br /&gt;
&amp;lt;source lang=sql&amp;gt;&lt;br /&gt;
SHOW WARNINGS;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
mysql&amp;gt; select * from `32e9e86e_f57f_4156_8d1a_26f169b845c4`;&lt;br /&gt;
Empty set, 2 warnings (0.00 sec)&lt;br /&gt;
&lt;br /&gt;
mysql&amp;gt; show warnings;&lt;br /&gt;
+---------+------+---------------------------------------------------------------------------------------------+&lt;br /&gt;
| Level   | Code | Message                                                                                     |&lt;br /&gt;
+---------+------+---------------------------------------------------------------------------------------------+&lt;br /&gt;
| Warning | 1292 | Incorrect datetime value: '32e9e86e-f57f-4156-8d1a-26f169b845c4' for column 'time' at row 1 |&lt;br /&gt;
| Warning | 1292 | Incorrect datetime value: '32e9e86e-f57f-4156-8d1a-26f169b845c4' for column 'time' at row 1 |&lt;br /&gt;
+---------+------+---------------------------------------------------------------------------------------------+&lt;br /&gt;
2 rows in set (0.00 sec)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== ETC ==&lt;br /&gt;
=== Checksum ===&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CHECKSUM TABLE tbl_name [, tbl_name] ... [QUICK | EXTENDED]&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
CHECKSUM TABLE reports a checksum for the contents of a table. You can use this statement to verify that the contents are the same before and after a backup, rollback, or other operation that is intended to put the data back to a known state.&lt;br /&gt;
&lt;br /&gt;
=== Check table exist ===&lt;br /&gt;
특정 테이블이 있는지, 아닌지를 알고 싶을 때가 있다. 이런 경우, 다음의 쿼리를  입력하면, 테이블이 있으면 정상, 없으면 오류를 리턴한다.&amp;lt;ref&amp;gt;http://stackoverflow.com/questions/8829102/mysql-check-if-table-exists-without-using-select-from&amp;lt;/ref&amp;gt;&lt;br /&gt;
&amp;lt;source lang=sql&amp;gt;&lt;br /&gt;
SELECT 1 FROM testtable LIMIT 1;&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Execute sql script ===&lt;br /&gt;
SQL 스크립트를 실행하고자 할 때는 다음과 같이 하면 된다.&amp;lt;ref&amp;gt;https://dev.mysql.com/doc/refman/5.7/en/mysql-batch-commands.html&amp;lt;/ref&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
-- shell&lt;br /&gt;
shell&amp;gt; mysql db_name&lt;br /&gt;
shell&amp;gt; mysql db_name &amp;lt; text_file&lt;br /&gt;
shell&amp;gt; mysql &amp;lt; text_file&lt;br /&gt;
&lt;br /&gt;
-- mysql&lt;br /&gt;
mysql&amp;gt; source file_name&lt;br /&gt;
mysql&amp;gt; \. file_name&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Create Primary Key with UUID() ===&lt;br /&gt;
uuid 를 primary key로 지정할 때, UUID() 함수를 사용할 수 있다. trigger 를 만들어 놓으면, 매번 테이블에 데이터를 입력할 때마다 자동으로 UUID() 함수가 실행되어 입력되도록 할 수도 있다.&amp;lt;ref&amp;gt;http://forums.mysql.com/read.php?10,175939,176708#msg-176708&amp;lt;/ref&amp;gt;&lt;br /&gt;
&amp;lt;source lang=sql&amp;gt;&lt;br /&gt;
DROP TABLE IF EXISTS t;&lt;br /&gt;
CREATE TABLE t(id CHAR(128));&lt;br /&gt;
&lt;br /&gt;
DELIMITER //&lt;br /&gt;
CREATE TRIGGER init_uuid BEFORE INSERT ON t&lt;br /&gt;
  FOR EACH ROW SET NEW.id = UUID();&lt;br /&gt;
//&lt;br /&gt;
DELIMITER ;&lt;br /&gt;
&lt;br /&gt;
INSERT INTO t VALUES( NULL );&lt;br /&gt;
SELECT * FROM t;&lt;br /&gt;
+--------------------------------------+&lt;br /&gt;
| id                                   |&lt;br /&gt;
+--------------------------------------+&lt;br /&gt;
| 42114800-6f22-1000-9611-2eb9174826e3 |&lt;br /&gt;
+--------------------------------------+&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== MAX_EXECUTION_TIME ===&lt;br /&gt;
Mysql 5.7 버전부터 사용할 수 있다. select 및 다른 SQL query 실행에 timeout 을 설정하는 옵션이다.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;source lang=sql&amp;gt;&lt;br /&gt;
SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM calls where destination = '11234';&lt;br /&gt;
&lt;br /&gt;
SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM t1 INNER JOIN t2 WHERE ...&lt;br /&gt;
&amp;lt;/source&amp;gt;&lt;br /&gt;
* https://dev.mysql.com/doc/refman/5.7/en/optimizer-hints.html#optimizer-hints-execution-time&lt;br /&gt;
* https://dev.mysql.com/doc/refman/5.7/en/server-system-variables.html&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* http://sjs0270.tistory.com/54 - 뷰(View)&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
&amp;lt;references /&amp;gt;&lt;br /&gt;
&lt;br /&gt;
[[category:mysql]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Kamailio_module_tls&amp;diff=3961</id>
		<title>Kamailio module tls</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Kamailio_module_tls&amp;diff=3961"/>
		<updated>2023-05-26T01:43:47Z</updated>

		<summary type="html">&lt;p&gt;Pchero: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Kamailio's TLS 모듈 내용 정리.&lt;br /&gt;
&lt;br /&gt;
The TLS(Transport Layer Security) module in Kamailio enables secure communication between SIP clients and servers using the TLS protocol.&lt;br /&gt;
&lt;br /&gt;
== TLS ciphers ==&lt;br /&gt;
When configuring the TLS module in Kamailio, user has the option to specify the cipher suites that will be used for securing the communication. Cipher suites are sets of cryptographic algorithms used for encryption, authentication, and key exchange during the TLS handshake process.&lt;br /&gt;
&lt;br /&gt;
Kamailio's TLS module supports various cipher suites, and the user can define the desired cipher list in the Kamailio configuration file. The cipher list determines the order and preference of cipher suites that Kamailio will negotiate with the connecting clients.&lt;br /&gt;
&lt;br /&gt;
* Default Cipher List&lt;br /&gt;
: The default cipher list used by Kamailio's TLS module is determined by the underlying TLS library(such as OpenSSL). It usually includes a broad range of cipher suites, prioritizing security and compatibility. The default cipher list can be overridden in the Kamailio configuration file.&lt;br /&gt;
&lt;br /&gt;
* Custom Cipher List&lt;br /&gt;
: User can define a custom cipher list in the Kamailio configuration file by setting the 'tls_ciphers' parameter. The cipher list should be a space-separated list of cipher suite names or aliases.&lt;br /&gt;
: &amp;lt;pre&amp;gt;modparam(&amp;quot;tls&amp;quot;, &amp;quot;tls_ciphers&amp;quot;, &amp;quot;ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-GCM-SHA256&amp;quot;)&amp;lt;/pre&amp;gt;&lt;br /&gt;
: In this example, the custom cipher list includes 3 cipher suites: ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-RSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES128-GCM-SHA256. Kamailio will attempt to negotiate these cipher suites in the specified order.&lt;br /&gt;
&lt;br /&gt;
* Restricting Cipher Suites&lt;br /&gt;
: If user wants to restrict the available cipher suites to a specific subset, user can define an explicit cipher list. This can be useful for enforcing stronger security measures or ensuring compatibility with specific requirements.&lt;br /&gt;
: &amp;lt;pre&amp;gt;modparam(&amp;quot;tls&amp;quot;, &amp;quot;tls_ciphers&amp;quot;, &amp;quot;HIGH:!aNULL:!MD5:!RC4&amp;quot;)&amp;lt;/pre&amp;gt;&lt;br /&gt;
: In this example, the cipher list restrict the available cipher suites to those considered to have high security strength and excludes certain weak or deprecated algorithms, such as 'aNULL'(no authentication), 'MD5', and 'RC4'.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://kamailio.org/docs/modules/5.3.x/modules/tls.html&lt;br /&gt;
&lt;br /&gt;
[[category:kamailio]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=Kamailio_module_tls&amp;diff=3960</id>
		<title>Kamailio module tls</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=Kamailio_module_tls&amp;diff=3960"/>
		<updated>2023-05-26T01:28:20Z</updated>

		<summary type="html">&lt;p&gt;Pchero: Created page with &amp;quot;== Overview == Kamailio's TLS 모듈 내용 정리.  The TLS(Transport Layer Security) module in Kamailio enables secure communication between SIP clients and servers using the TLS protocol.  == TLS ciphers == When configuring the TLS module in Kamailio, user has the option to specify the cipher suites that will be used for securing the communication. Cipher suites are sets of cryptographic algorithms used for encryption, authentication, and key exchange during the TLS h...&amp;quot;&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
Kamailio's TLS 모듈 내용 정리.&lt;br /&gt;
&lt;br /&gt;
The TLS(Transport Layer Security) module in Kamailio enables secure communication between SIP clients and servers using the TLS protocol.&lt;br /&gt;
&lt;br /&gt;
== TLS ciphers ==&lt;br /&gt;
When configuring the TLS module in Kamailio, user has the option to specify the cipher suites that will be used for securing the communication. Cipher suites are sets of cryptographic algorithms used for encryption, authentication, and key exchange during the TLS handshake process.&lt;br /&gt;
&lt;br /&gt;
Kamailio's TLS module supports various cipher suites, and the user can define the desired cipher list in the Kamailio configuration file. The cipher list determines the order and preference of cipher suites that Kamailio will negotiate with the connecting clients.&lt;br /&gt;
&lt;br /&gt;
* Default Cipher List&lt;br /&gt;
: The default cipher list used by Kamailio's TLS module is determined by the underlying TLS library(such as OpenSSL). It usually includes a broad range of cipher suites, prioritizing security and compatibility. The default cipher list can be overridden in the Kamailio configuration file.&lt;br /&gt;
&lt;br /&gt;
* Custom Cipher List&lt;br /&gt;
: User can define a custom cipher list in the Kamailio configuration file by setting the 'tls_ciphers' parameter. The cipher list should be a space-separated list of cipher suite names or aliases.&lt;br /&gt;
: &amp;lt;pre&amp;gt;modparam(&amp;quot;tls&amp;quot;, &amp;quot;tls_ciphers&amp;quot;, &amp;quot;ECDHE-ECDSA-AES256-GCM-SHA384 ECDHE-RSA-AES256-GCM-SHA384 ECDHE-ECDSA-AES128-GCM-SHA256&amp;quot;)&amp;lt;/pre&amp;gt;&lt;br /&gt;
: In this example, the custom cipher list includes 3 cipher suites: ECDHE-ECDSA-AES256-GCM-SHA384, ECDHE-RSA-AES256-GCM-SHA384, ECDHE-ECDSA-AES128-GCM-SHA256. Kamailio will attempt to negotiate these cipher suites in the specified order.&lt;br /&gt;
&lt;br /&gt;
* Restricting Cipher Suites&lt;br /&gt;
: If user wants to restrict the available cipher suites to a specific subset, user can define an explicit cipher list. This can be useful for enforcing stronger security measures or ensuring compatibility with specific requirements.&lt;br /&gt;
: &amp;lt;pre&amp;gt;modparam(&amp;quot;tls&amp;quot;, &amp;quot;tls_ciphers&amp;quot;, &amp;quot;HIGH:!aNULL:!MD5:!RC4&amp;quot;)&amp;lt;/pre&amp;gt;&lt;br /&gt;
: In this example, the cipher list restrict the available cipher suites to those considered to have high security strength and excludes certain weak or deprecated algorithms, such as 'aNULL'(no authentication), 'MD5', and 'RC4'.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Kamailio&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=SIP_Header&amp;diff=3959</id>
		<title>SIP Header</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=SIP_Header&amp;diff=3959"/>
		<updated>2023-04-20T08:25:27Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* Session-Expires */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
SIP headers 내용 정리&lt;br /&gt;
&lt;br /&gt;
== Basic ==&lt;br /&gt;
=== SIP and SIPS URIs ===&lt;br /&gt;
SIP URIs are used in a number of places including the To, From, and Contact headers, as well as in the Request-URI, which indicates the destination. SIP URIs are similar to the mailto URL and can be used in hyperlinks on Web page, for example. They can also include telephone numbers. The information in a SIP URL incidates the way in which the resource (user) should be contacted using SIP.&lt;br /&gt;
&lt;br /&gt;
An example SIP URI contains the scheme '''sip''' a ''':''', then a '''username@host''' or IPv4 or IPv6 address followed by an optional ''':''', then port number, or a list of ''';''' separated URI parameters.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sip:pchero.sungtae@pchero21.com:5060;transport=udp;user=ip;method=INVITE;ttl=1;maddr=240.101.102.103?Subject=FFT&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Note that URIs may not contain spaces or line breaks, so this example would be on a single line. Some SIP URIs, such as REGISTER Request-URI do not have a username, but begin with the host or IP address. In this example, the port number is shown as 5060, the well-known port number for SIP. For a SIP number 5061 is assumed. The transport parameter indicates UDP is to be used, which is the default. TCP, TLS, and SCTP are alternative transport parameters.&lt;br /&gt;
&lt;br /&gt;
The user parameter is used by parsers to determine if a telephone number is present in the username portion of the URI. The assumed default is that it is not, indicated by the value ip. If a telephone. number is present, it is indicated by the value phone. This parameter must not used to guess at the characteristics or capabilities of the user agent. For example, the presence of a user=phone parameter must not be interpreted that the user agent is a SIP telephone(which may have limited display and processing capabilties). In a telephone environment, IP telephones and IP/PSTN gateways may in fact use the reverse assumption, interpreting any digits in a username as digits regardless of the presence of user=phone.&lt;br /&gt;
&lt;br /&gt;
== CSeq ==&lt;br /&gt;
The CSeq header field serves as a way to identify and order transactions. It consists of a sequence number and a method. The method MUST match that of the request. For non-REGISTER requests outside of a dialog, the sequence number value is arbitrary. The sequence number value MUST be expressible as a 32-bit unsigned integer and MUST be less than 2**31. As long as it follows the above guidelines, a client may use any mechanism it would like to select CSeq header field values.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CSeq: 4711 INVITE&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
CSeq 는 메시지의 순서를 파악하는데 사용된다. 각각의 request 메시지마다 1씩 증가하게 되며, 이를 통해서 메시지의 순서를 확인할 수 있다. 만약 이전에 처리한 CSeq 보다 낮은 숫자의 Request 가 오게된다면 500 response 를 보내야 한다. 하지만 CSeq 는 종종 한번에 1 이상씩 증가할 수도 있다. 하지만 이는 에러가 아니며, 메시지는 정상적으로 처리되게 된다.&lt;br /&gt;
&lt;br /&gt;
=== UAC behavior ====&lt;br /&gt;
The Call-ID of the request MUST be set to the Call-ID of the dialog. Requests within a dialog MUST contain strictly monotonically increasing and contiguous CSeq sequence numbers (increasing-by-one) in each direction (excepting ACK and CANCEL of course, whose numbers equal the requests being acknowledged or canceled). Therefore, if the local sequence number is not empty, the value of the local sequence number MUST be incremented by one, and this value MUST be placed into the CSeq header field. If the local sequence number is empty, an initial value MUST be chosen using the guidelines.&lt;br /&gt;
&lt;br /&gt;
=== UAS behavior ===&lt;br /&gt;
If the remote sequence number is empty, it MUST be set to the value of the sequence number in the CSeq header field value in the request. If the remote sequence number was not empty, but the sequence number of the request is lower than remote sequence number, the request is out of order and MUST be rejected with a 500 (Server internal Error) response. If the remote sequence number was not empty, and the sequence number of the request is greater than the remote sequence number, the request is in order. It is possible for the CSeq sequence number to be higher than the remote sequence umber by more than one. This is not an error condition, and a UAS SHOULD be prepared to receive and process requests with CSeq values more than one higher than the previous received request. The UAS MUST then set the remote sequence number to the value of the sequence number in the CSeq header field value in the request.&lt;br /&gt;
&lt;br /&gt;
: If a proxy challenges a request generated by the UAC, the UAC has to resubmit the request with credentials. The resubmitted request will have a new CSeq number. The UAS will never see the first request, and thus, it will notice a gap in the CSeq number space. Such a gap does not represent any error condition.&lt;br /&gt;
&lt;br /&gt;
When a UAS receives a target refresh request, it MUST replace the dialog's remote target URI with the URI from the Contact header field in the request, if present.&lt;br /&gt;
&lt;br /&gt;
== Max-Forwards ==&lt;br /&gt;
The Max-Forwards header field is used to indicate the maximum number of hops that a SIP request may take. The value of the header field is decremented by each proxy that forwards the request. A proxy receiving the header field with a value of zero discards the message and sends a 403 Too Many Hops response back to the originator.&lt;br /&gt;
&lt;br /&gt;
Max-Forwards is a mandatory header field in requests generated by an RFC 3261 compliant UA. However, an RFC 2543 UA generally will not include the header field. The suggested initial value is 70 hops.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Max-Forwards: 70&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== P-Asserted-Identity ==&lt;br /&gt;
P-Asserted-Identity is a special type of UA identity implying &amp;quot;This is the proven ID for me within this trusted network&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Usually, UA ID is set in 'From' header, but the From header does not necessarily contain the actual identity. Actually, you can put any kind of string for 'From' header. Therefore, once the authentication is complete for a UA, a proxy can add P-Asserted-Identity to inform other proxies saying 'Hey, this is a proven ID. So you can trust'.&lt;br /&gt;
&lt;br /&gt;
From the RFC 3325, 9.1 The P-Asserted-Identity header:&lt;br /&gt;
: The P-Asserted-Identity header field is used among trusted SIP entities (typically intermediaries)to carry the identity of the user sending a SIP message as it was verified by authentication.&lt;br /&gt;
: A P-Asserted-Identity header field value MUST consist of exactly one name-addr or addr-spec. There may be one or two P-Asserted-Identity values. If there is one value, it MUST be a sip, or tel URI. If there are two values, one value MUST be a sip or sips URI and the other MUST be a tel URI. It is worth noting that proxies can (and will) add and remove this header field.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
P-Asserted-Identity: tel:+123456789&lt;br /&gt;
P-Asserted-Identity: &amp;quot;sungtae kim&amp;quot; &amp;lt;sip:pchero@pchero21.com&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* https://i3forum.org/wp-content/uploads/2019/06/i3f_CLI_management_guidelines_rev-1.pdf&lt;br /&gt;
&lt;br /&gt;
== Record-Route ==&lt;br /&gt;
The Record-Route header field is used to force routing through a proxy for all subsequent requests in a session(dialog) between two UAs. Normally, the presence of a Contact header field allows UAs to send messages directly bypassing the proxy chain used in the initial request (which probably involved database lookups to locate the called party). A proxy inserting its address into a Record-Route header field overrides this and forces future requests to include a Route header field containing the address of the proxy that forces this proxy to be included.&lt;br /&gt;
&lt;br /&gt;
A proxy  wishing to implement this inserts the header field containing its own URI, or adds it URI to an already present Record-Route header field. The URI is constructed so that the URI resolves back to the proxy server. The UAS copies the Record-Route header field into the 200 OK response to the request. The header field is forwarded unchanged by proxies back to the UAC. The UAC then stores the Record-Route proxy list plus a Contact header field if present in the 200 OK for use in a Route header field in all subsequent requests. Because Record-Route is bidirectional, messages in the reverse direction will also traverse the same set of proxies.&lt;br /&gt;
&lt;br /&gt;
The 'lr' parameter is new to RFC 3261 and indicates that the proxy server supports &amp;quot;loose routing&amp;quot;.&lt;br /&gt;
&lt;br /&gt;
Older RFC 2543 compliant proxy servers create Record-Route URIs that instead of the lr parameter often contain the maddr parameter with an address or host that resolves to that proxy server.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Record-Route: &amp;lt;sip:10.164.0.8;transport=tcp;lr;r2=on&amp;gt;&lt;br /&gt;
Record-Route: &amp;lt;sip:192.168.0.237;transport=tcp;lr;r2=on&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Record-Route: &amp;lt;sip:10.164.0.8;transport=tcp;lr;r2=on&amp;gt;,&amp;lt;sip:192.168.0.237;transport=tcp;lr;r2=on&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Request line URI ==&lt;br /&gt;
The Request line URI includes the destination of the call. It contains the same information as the To field, omitting the display name.&lt;br /&gt;
&lt;br /&gt;
== Route ==&lt;br /&gt;
The Route header field is used to provide routing information for requests. RFC 3261 introduces two types of routing: strict and loose routing, which have a similar meaning as the IP routing modes of the same name. In strict routing, a proxy must use the first URI in the Route header field to rewrite the Request-URI, which is then forwarded. In loose routing, a proxy does not rewrite the Request-URI, but either forwards the request to the first URI in the Route header field or to another loose routing element.&lt;br /&gt;
&lt;br /&gt;
In loose routing, the request must route through every server in the Route list(but may also route through other servers) before it may be routed based on the Request-URI.&lt;br /&gt;
&lt;br /&gt;
In strict routing, the request must only route through the set of server in the Route header field with the Request-URI being rewritten at each hop.&lt;br /&gt;
&lt;br /&gt;
A proxy or UAC can tell if the next element in the route set supports loose routing by the presence of an 'lr' parameter.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Route: &amp;lt;sip:192.168.0.237;transport=tcp;lr;r2=on&amp;gt;&lt;br /&gt;
Route: &amp;lt;sip:10.164.0.8;transport=tcp;lr;r2=on&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Route: &amp;lt;sip:192.168.0.237;transport=tcp;lr;r2=on&amp;gt;, Route: &amp;lt;sip:10.164.0.8;transport=tcp;lr;r2=on&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Session-Expires ==&lt;br /&gt;
The Session-Expires header field is used to specify the expiration time of the session. To extend the session, either UA can send a re-INVITE or UPDATE with a new Session-Expires header field. &lt;br /&gt;
&lt;br /&gt;
At the expiration of the interval in the Session-Expires, either UA may send a BYE and call-stateful proxies may destroy any state information. A proxy may shorten the expiration time by reducing the interval in the header field as it proxies the request.&lt;br /&gt;
&lt;br /&gt;
A UAS confirms the session timer by including the Session-Expires header field in the response to the request. A UAS may also shorten the interval by reducing the interval.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
Session-Expires: 1800&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Btw, the UA can expire the session before the session expiration. The RFC-4028 recommends 32-secs for early expiration. Here is a detailed description. In this case, the UA should send the BYE.&lt;br /&gt;
&lt;br /&gt;
https://www.rfc-editor.org/rfc/rfc4028.html#section-10&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
   Similarly, if the side not performing refreshes does not receive a&lt;br /&gt;
   session refresh request before the session expiration, it SHOULD send&lt;br /&gt;
   a BYE to terminate the session, slightly before the session&lt;br /&gt;
   expiration.  The minimum of 32 seconds and one third of the session&lt;br /&gt;
   interval is RECOMMENDED.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
And the Asterisk(PJSIP) follows this 32-sec recommendation.&lt;br /&gt;
&lt;br /&gt;
https://github.com/pjsip/pjproject/blob/master/pjsip/src/pjsip-ua/sip_timer.c#L518&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    } else {&lt;br /&gt;
        /* Send BYE if no refresh received until this timer fired, delay&lt;br /&gt;
         * is the minimum of 32 seconds and one third of the session interval&lt;br /&gt;
         * before session expiration.&lt;br /&gt;
         */&lt;br /&gt;
        delay.sec = timer-&amp;gt;setting.sess_expires - &lt;br /&gt;
                    timer-&amp;gt;setting.sess_expires/3;&lt;br /&gt;
        delay.sec = PJ_MAX((long)timer-&amp;gt;setting.sess_expires-32, delay.sec);&lt;br /&gt;
    }&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== To ==&lt;br /&gt;
The To header field is required header field in every SIP message used to indicate the recipient of the request. Any responses generated by a UA will contain this header field with the addition of a tag. (Note tha t an RFC 2543 client will typically only generate a tag if more than one Via header field is present in the request.) Any response generated by a proxy must have a tag added to the To header field. A tag added to the header field in a 200 OK response is used throughout the call and incorporated into the dialog. The To header field URI is never used for routing - the request-URI is used for this purpose. An optional display name can be present in the header field, in which case the SIP URI is enclosed in &amp;lt; &amp;gt;. If the URI contains any parameters or username parameters, the URI must be enclosed in &amp;lt; &amp;gt; even if no display name is present. The compact form of the header fields is t.&lt;br /&gt;
&lt;br /&gt;
== Via ==&lt;br /&gt;
Every proxy in the request path adds to top of the &amp;quot;Via&amp;quot; the address and port on which it received the message, than forwards it onwards. When processing responses, each proxy in the return path process the contents of the &amp;quot;Via&amp;quot; field in reverse order, removing its address from the top.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://www.3cx.com/blog/voip-howto/sip-invite-header-fields - The main SIP INVITE Header Fields explained&lt;br /&gt;
&lt;br /&gt;
[[category:SIP protocol]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
	<entry>
		<id>http://wiki.pchero21.com/index.php?title=SIP_RTP&amp;diff=3958</id>
		<title>SIP RTP</title>
		<link rel="alternate" type="text/html" href="http://wiki.pchero21.com/index.php?title=SIP_RTP&amp;diff=3958"/>
		<updated>2023-02-28T13:57:11Z</updated>

		<summary type="html">&lt;p&gt;Pchero: /* RTP Header */&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;== Overview ==&lt;br /&gt;
RTP 내용정리&lt;br /&gt;
&lt;br /&gt;
== Basic ==&lt;br /&gt;
실시간 음성, 영상 데이터를 IP 네트워크 상에서 전송하기 위해서는 항상 RTP 를 사용한다. RTP는 RFC 1889(A Transport Protocol for Real-Time Applications)에 정의되어 있었지만, 2003년 RFC 1889를 대신하는 RFC 3550이 standards track 으로 채택 되면서, RFC 1889 가 폐기되었다.&lt;br /&gt;
&lt;br /&gt;
간략하게 RFC 3550의 앞부분에 명시된 개요 부분을 요약하면, RTP는 Real-Time Transport Protocol의 약어로 음성, 영상 또는 시뮬레이션 데이터와 같은 실시간 데이터를 멀티캐스트 또는 유니캐스트 네트워크를 이용하여 전송하는 응용 서비스를 위한 end-to-end 네트워크 전송 프로토콜이다. RTP는 RTCP(Real-Time Control Protocol)에 의해 데이터의 전달 상황을 감시하며, 최소한의 제어 기능과 미디어 식별 기능을 제공한다.&lt;br /&gt;
&lt;br /&gt;
== Protocol ==&lt;br /&gt;
RTP는 전송 프로토콜로 UDP(User Datagram Protocol)과 네트워크 프로토콜로 IP를 이용한다. RTP가 신뢰할 수 있는 TCP를 이용하지 않고 UDP를 이용하는 이유는 실시간 음성 및 영상은 패킷 에러나 패킷 소실이 발생하더라도 TCP 재전송 매커니즘을 활용할 수 없기 때문이다. 재전송된 패킷은 수신 단말이 재생해야 할 시점을 이미 지나버린 이후가 될 확률이 높아 패킷을 폐기한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
RTP 패킷당 페이로드 크기&lt;br /&gt;
--------------------------------------------------------------&lt;br /&gt;
| Ethernet(18 bytes) | IP(20 bytes) | RTP(12 bytes) | Sample |&lt;br /&gt;
--------------------------------------------------------------&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
섀논과 나이키스트 정리에 의해 G.711 코덱은 1초당 8000 개의 샘플링과 샘플당 8비트로 양자화한다. DSP 칩으로 G.711 코덱을 적용하면 10ms 단위당 음성 샘플을 160 바이트이고, G.729는 20 바이트이다. 만일 패킷당 10ms 단위로 페이로드를 만들면 초당 100개가 전송되고, 패킷당 20ms 단위로 페이로드를 만들면 50개가 전송된다. 일반적으로 20ms 단위로 하나의 패킷을 만들어 초당 50개의 패킷을 생성한다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
G.711&lt;br /&gt;
&lt;br /&gt;
1 초당 8000 개의 샘플링&lt;br /&gt;
샘플당 8 비트(bit)&lt;br /&gt;
1초 음성에 사용되는 바이트(byte) = 8000 * 8(bit) / 8(byte 계산) = 8000 byte = 8Kb&lt;br /&gt;
만약 스테레오라면 8Kb x 2 = 16Kb&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== RTP Header ==&lt;br /&gt;
RFC 3550에 RTP 헤더가 정의되어 있다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
    0                   1                   2                   3&lt;br /&gt;
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
   |V=2|P|X|  CC   |M|     PT      |       sequence number         |&lt;br /&gt;
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
   |                           timestamp                           |&lt;br /&gt;
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
   |           synchronization source (SSRC) identifier            |&lt;br /&gt;
   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+&lt;br /&gt;
   |            contributing source (CSRC) identifiers             |&lt;br /&gt;
   |                             ....                              |&lt;br /&gt;
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* V(version)&lt;br /&gt;
: 2 Bit.&lt;br /&gt;
: RTP 의 Version 표시. 현재 버전은 2.&lt;br /&gt;
&lt;br /&gt;
* P(padding)&lt;br /&gt;
: 1 bit.&lt;br /&gt;
: 패킷의 마지막 부분에 하나 이상의 패딩 바이트 유무 표시.&lt;br /&gt;
: 패딩 비트는 의미가 없는 비트로 헤더나 패킷의 크기를 일정하게 유지하기 위해 사용하는 비트.&lt;br /&gt;
&lt;br /&gt;
* X (extension)&lt;br /&gt;
: 1 bit.&lt;br /&gt;
: 고정 헤더 이후의 하나 이상의 확장 헤더 유무 표시.&lt;br /&gt;
&lt;br /&gt;
* CC (CSRC Count)&lt;br /&gt;
: 4 bit.&lt;br /&gt;
: RTP 12 바이트 고정 헤더 위에 CSRC identifier 의 수 표시.&lt;br /&gt;
&lt;br /&gt;
* M (Marker)&lt;br /&gt;
: 1 bit.&lt;br /&gt;
: 패킷 내에서 프레임 경계와 같은 중요한 이벤트들을 표시.&lt;br /&gt;
: Payload Type 필드의 확장을 위해 무시되기도 함.&lt;br /&gt;
: The interpretation of the marker is defined by a profile. It is intended to allow significant events such as frame boundaries to be marked in the packet stream. A profile MAY define additional bits or specify that there is no marker bit by changing the number of bits in the payload type field.&lt;br /&gt;
&lt;br /&gt;
* PT (Payload Type)&lt;br /&gt;
: 7 bit.&lt;br /&gt;
: 페이로드 타입은 RTP가 전송하고 있는 실시간 데이터의 압축 코덱을 명시.&lt;br /&gt;
: 페이로드 타입은 Capability Exchange 협상에서 상호 인지 필수.&lt;br /&gt;
: This field identifies the format of the RTP payload and determines its interpretation by the application. A profile MAY specify a default static mapping of payload type codes to payload formats. Additional payload type codes MAY be defined dynamically through non-RTP means. A set of default mappings for audio and video is specified in the companion RFC 3551. An RTP source MAY change the payload type during a session, but this field SHOULD NOT be used for multiplexing separate media streams. A receiver MUST ignore packets with payload types that it does not understand.&lt;br /&gt;
&lt;br /&gt;
* Sequence number&lt;br /&gt;
: 16 bit.&lt;br /&gt;
: 보안을 이유로 랜덤 번호에서 시작하고 패킷이 늘어날 때마다 1씩 증가.&lt;br /&gt;
: 수신 측이 패킷 손실 여부 확인 가능.&lt;br /&gt;
: The sequence number increments by one for each RTP data packet sent, and may be used by the receiver to detect packet loss and to restore packet sequence. The initial value of the sequence number SHOULD be random(unpredictable) to make known-plaintext attacks on encryption more difficult, even if the source itself does not encrypt, because the packets may flow through a translator that does.&lt;br /&gt;
&lt;br /&gt;
* Timestamp&lt;br /&gt;
: 32 bit.&lt;br /&gt;
: RTP 패킷의 첫 번째 바이트의 샘플링 순간을 표시.&lt;br /&gt;
: 초기값은 랜덤 넘버로 결정되지만 샘플링 레이트에 따라 증가량은 상이함.&lt;br /&gt;
: The timestamp reflects the sampling instant MUST be derived from a clock that increments monotonically and linearly in time to allow synchronization and jitter calculations. The resolution of the clock MUST be sufficient for the desired&lt;br /&gt;
&lt;br /&gt;
* SSRC (Synchronization Source) Identifier&lt;br /&gt;
: 32 bit.&lt;br /&gt;
: 동기화 소스로 랜덤 넘버로 결정.&lt;br /&gt;
&lt;br /&gt;
* CSRC (Contributing Source) Identifier&lt;br /&gt;
: 32 bit.&lt;br /&gt;
: 다수의 음원이 Audio Mixer 를 통해 하나로 통합될 경우 원래 음원의 목록을 표시.&lt;br /&gt;
&lt;br /&gt;
== RTCP Header ==&lt;br /&gt;
RFC 3550에 RTCP 헤더가 정의되어 있다.&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
        0                   1                   2                   3&lt;br /&gt;
        0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1&lt;br /&gt;
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
header |V=2|P|    RC   |   PT=SR=200   |             length            |&lt;br /&gt;
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
       |                         SSRC of sender                        |&lt;br /&gt;
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+&lt;br /&gt;
sender |              NTP timestamp, most significant word             |&lt;br /&gt;
info   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
       |             NTP timestamp, least significant word             |&lt;br /&gt;
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
       |                         RTP timestamp                         |&lt;br /&gt;
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
       |                     sender's packet count                     |&lt;br /&gt;
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
       |                      sender's octet count                     |&lt;br /&gt;
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+&lt;br /&gt;
report |                 SSRC_1 (SSRC of first source)                 |&lt;br /&gt;
block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
  1    | fraction lost |       cumulative number of packets lost       |&lt;br /&gt;
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
       |           extended highest sequence number received           |&lt;br /&gt;
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
       |                      interarrival jitter                      |&lt;br /&gt;
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
       |                         last SR (LSR)                         |&lt;br /&gt;
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
       |                   delay since last SR (DLSR)                  |&lt;br /&gt;
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+&lt;br /&gt;
report |                 SSRC_2 (SSRC of second source)                |&lt;br /&gt;
block  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
  2    :                               ...                             :&lt;br /&gt;
       +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+&lt;br /&gt;
       |                  profile-specific extensions                  |&lt;br /&gt;
       +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== SRTP Decode ==&lt;br /&gt;
=== libsrtp ===&lt;br /&gt;
* https://github.com/cisco/libsrtp&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
./configure --enable-openssl --enable-log-stdout --with-openssl-dir=/opt/homebrew/opt/openssl@3 --prefix=/usr/local&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
libsrtp provides useful utilities under the test folder.&lt;br /&gt;
* rtp_decoder: Performs the description. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Troubleshooting ==&lt;br /&gt;
* RTP 패킷이 정상적으로 수신되는가?&lt;br /&gt;
: 시그널링이 정상적이어도 RTP 패킷이 송수신되지 않는 경우가 있다. 실제 NAT(Network Address Translation) 이슈로 인해 주소가 잘못 지정되었을 수도 있다.&lt;br /&gt;
&lt;br /&gt;
* SSRC 가 변경되지는 않았는가?&lt;br /&gt;
: 같은 통화의 패킷임에도 SSRC가 변경되면 다른 세션으로 인식되어 패킷은 폐기된다.&lt;br /&gt;
&lt;br /&gt;
* 시퀀스 넘버는 일정하게 증가하는가?&lt;br /&gt;
: 시퀀스 넘버가 순차적으로 증가하지않고 갑자기 증가하거나 감소하는 경우가 있다. 예를 들어 100 다음 400번이 오는 경우 수신 단말은 300 개의 패킷이 손실된 것으로 보고 재생하지 않는다. 반대로 100 다음에 60 이 오는 경우 수신단말은 이미 수신한 패킷으로 인식하고 재생하지 않는다.&lt;br /&gt;
: RFC 3550 Appendix A.3 Determining Number of Packets Expected and Lost 에 의하면, 갑자기 비연속적인 시퀀스 넘버가 3개 이상 연속될 경우 수신 단말은 시퀀스 넘버가 재설정된 것으로 인삭하고 재생해야 한다.&lt;br /&gt;
&lt;br /&gt;
* 타임스탬프는 일정하게 증가하는가?&lt;br /&gt;
: 타임스탬프는 패킷의 재생 시점을 확인하므로 갑자기 감소하거나 크게 증가하면 안된다.&lt;br /&gt;
&lt;br /&gt;
* 음질에 문제가 있는가?&lt;br /&gt;
: 와이어 샤크로 RTP 패킷을 캡쳐하여 음성 파일로 만들어서 재생할 수 있다. 음성 파일에 문제가 있다면 송신 단말에서 문제가 있는 것이고, 음성 파일에 문제가 없다면 수신 단말의 문제이다.&lt;br /&gt;
&lt;br /&gt;
== See also ==&lt;br /&gt;
* https://brunch.co.kr/@linecard/154 - RTP의 이해&lt;br /&gt;
&lt;br /&gt;
[[category:SIP protocol]]&lt;/div&gt;</summary>
		<author><name>Pchero</name></author>
	</entry>
</feed>